ATcoder368D題詳解

一只小絮絮發表於2024-08-25

D題傳送門

一道很無腦的題,但考試沒寫出來

爆搜

首先看樸素演算法

1.從根節點開始遍歷每個節點

2.遇到要儲存的節點就進行標記,直到所有儲存節點都標記

時間複雜度 \(O(n)\)

其實已經能過了,但我沒用(doge)

樹鏈剖分(LCA)

首先分析

1.每一次砍掉枝葉,都是在沒有要儲存的節點存在子樹上時.

2.因此,我們可以將每一個要儲存的節點進行求最近公共祖先.

3.最後對每個節點進行遍歷,判斷是否有要儲存的節點在其子樹上.

4.如果有就加1,否則不變.

考慮最壞情況每個節點都遍歷,時間複雜度\(O(n log(n))\)

一般情況下時間複雜度是比爆搜要快的,也快不到哪去

#include<bits/stdc++.h>
#define N 500009
using namespace std;
struct intt{
	int next,to;
}a[N];
int h[N];
int cnt,b[N];
int vis[N];
int n,k;
int x,y,ans;
int fa[N],dep[N],sz[N];
int top[N],son[N]; 
void add(int u,int v){
	a[++cnt].next=h[u];
	a[cnt].to=v;
	h[u]=cnt;
}
void dfs1(int x,int f){
	fa[x]=f,dep[x]=dep[f]+1,sz[x]=1;
	for(int i=h[x];i;i=a[i].next){
		int y=a[i].to;
		if(y==f) continue;
		dfs1(y,x);
		sz[x]+=sz[y];
		if(sz[son[x]]<sz[y]) son[x]=y;
	} 
}

void dfs2(int x,int t){
	top[x]=t;
	if(!son[x]) return ;
	dfs2(son[x],t);
	for(int i=h[x];i;i=a[i].next){
		int y=a[i].to;
		if(y==fa[x]||y==son[x]) continue;
		dfs2(y,y);
	}
}

int LCA(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]<dep[y]? x:y;
}

void dfs3(int p){
	cout<<p<<endl;
	for(int i=h[p];i;i=a[i].next){
		int v=a[i].to;
		if(dep[v]<=dep[p]) continue;
		dfs3(v);
		if(vis[v]) vis[p]=1;
	}
}

int main(){
	cin>>n>>k;
	ans=n;
	for(int i=1;i<=n-1;i++){
		cin>>x>>y;
		add(x,y);
		add(y,x);
	}
	for(int i=1;i<=k;i++){ 
		cin>>b[i];
		vis[b[i]]++;
	}
	dfs1(1,0);
	dfs2(1,1);
	int t=b[1];
	for(int i=2;i<=k;i++){
		t=LCA(t,b[i]);
	}
	dfs3(t);
	for(int i=1;i<=n;i++)
		if(vis[i]) ans++;
	cout<<ans<<endl;
	return 0;
}

相關文章