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;
}