ATcoder368D题详解
一道很无脑的题,但考试没写出来
爆搜
首先看朴素算法
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;
}

浙公网安备 33010602011771号