lgswdn_SA
2020-05-09 11:13:45
对于一个非重心的节点
所以,我们选出的这个子树大小不能大于
一个节点被拿掉后的连通块可以分为两种:自己孩子的子树和其他部分。所以我们可以用二次扫描去做换根
我们记录
对于求子树中的
int f[N][2],mx[N];
void dfs1(int u,int fa){ //计算f[u][0/1]和mx[u]
for(int i=hd[u],v;i;i=e[i].nxt)
if((v=e[i].to)!=fa){
dfs1(v,u);
int tmp=(sz[v]>n/2?f[v][0]:sz[v]);
if(tmp>f[u][0]) f[u][1]=f[u][0],f[u][0]=tmp,mx[u]=v;
else if(tmp>f[u][1]) f[u][1]=tmp;
}
}
我们记录
对于求子树外的
int g[N];
void dfs2(int u,int fa){ //计算 g[u]
for(int i=hd[u],v;i;i=e[i].nxt)
if((v=e[i].to)!=fa){
int tmp=(n-sz[v]>n/2?g[u]:n-sz[v]);
g[v]=max(g[v],tmp);
if(v==mx[u]) g[v]=max(g[v],f[u][1]);
else g[v]=max(g[v],f[u][0]);
dfs2(v,u);
}
}
最后计算答案的时候还要记录一下自己为根时的有最大子树大小的儿子节点
总体代码:
#include<bits/stdc++.h>
using namespace std;
const int N=4e5+9;
struct edge{int to,nxt;}e[N*2]; int hd[N],tot;
void add(int u,int v){e[++tot]=(edge){v,hd[u]};hd[u]=tot;}
int n,sz[N],msz[N];
void pre(int u,int fa){ //计算sz[u]和msz[u]
for(int i=hd[u],v;i;i=e[i].nxt)
if((v=e[i].to)!=fa){
pre(v,u);
sz[u]+=sz[v];
if(sz[v]>sz[msz[u]]) msz[u]=v;
}
sz[u]++;// msz[u]=max(msz[u],n-sz[u]);
}
int f[N][2],mx[N];
void dfs1(int u,int fa){ //计算f[u][0/1]和mx[u]
for(int i=hd[u],v;i;i=e[i].nxt)
if((v=e[i].to)!=fa){
dfs1(v,u);
int tmp=(sz[v]>n/2?f[v][0]:sz[v]);
if(tmp>f[u][0]) f[u][1]=f[u][0],f[u][0]=tmp,mx[u]=v;
else if(tmp>f[u][1]) f[u][1]=tmp;
}
}
int g[N];
void dfs2(int u,int fa){ //计算 g[u]
for(int i=hd[u],v;i;i=e[i].nxt)
if((v=e[i].to)!=fa){
int tmp=(n-sz[v]>n/2?g[u]:n-sz[v]);
g[v]=max(g[v],tmp);
if(v==mx[u]) g[v]=max(g[v],f[u][1]);
else g[v]=max(g[v],f[u][0]);
dfs2(v,u);
}
}
int main(){
scanf("%d",&n);
for(int i=1,u,v;i<n;i++)
scanf("%d%d",&u,&v),add(u,v),add(v,u);
pre(1,0), dfs1(1,0), dfs2(1,0);
for(int i=1;i<=n;i++){
bool centroid=1;
if(sz[msz[i]]>n/2) centroid=(sz[msz[i]]-f[i][0]<=n/2);
else if(n-sz[i]>n/2) centroid=(n-sz[i]-g[i]<=n/2);
cout<<centroid<<" ";
}
return 0;
}