题意:
给你n个点的树(n<=4e5),对于每个点能否删掉一条边再添加一条边使得这个点成为重心(每一个子树的大小都小于等于n/2)
思路:
对于每个点u,判断有多少个子树的大小大于n/2
每个子树维护这个子树中节点数小于等于n/2的最大值,次大值以及它们对应的节点向上和向下dp一下就可以了
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=4e5+100;
struct Edge{
int to,next;
}e[N*2];
int tot,head[N],n,size[N],mx,a[N],up[N],up_max[N];
void init(){
tot=0;
memset(head,-1,sizeof(head));
}
void addedge(int from,int to){
e[tot]=(Edge){to,head[from]};
head[from]=tot++;
}
struct node1{
int maxv1,id1,maxv2,id2;
}b[N];
void getmx(int u,int num,int v){
if(b[u].maxv1<num){
b[u].maxv2=b[u].maxv1,b[u].id2=b[u].id1;
b[u].maxv1=num,b[u].id1=v;
}
else if(b[u].maxv2<num)
b[u].maxv2=num,b[u].id2=v;
}
void dfs1(int u,int pre){
size[u]=1,a[u]=1;
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(v==pre) continue;
dfs1(v,u);
a[u]=max(a[u],size[v]);
if(size[v]<=mx) getmx(u,size[v],v);
else getmx(u,b[v].maxv1,v);
size[u]+=size[v];
}
up[u]=n-size[u];
}
void dfs2(int u,int pre){
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(v==pre) continue;
if(b[u].id1!=v) up_max[v]=max(b[u].maxv1,up_max[v]);
else up_max[v]=max(b[u].maxv2,up_max[v]);
if(n-size[v]<=mx) up_max[v]=max(up_max[v],n-size[v]);
up_max[v]=max(up_max[v],up_max[u]);
dfs2(v,u);
}
}
int main(){
int u,v;
scanf("%d",&n);
mx=n/2,init();
for(int i=1;i<n;i++){
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
dfs1(1,0);
dfs2(1,0);
for(int i=1;i<=n;i++){
if(a[i]<=mx&&up[i]<=mx)
printf("1 ");
else if(a[i]>mx&&a[i]-b[i].maxv1<=mx)
printf("1 ");
else if(up[i]>mx&&up[i]-up_max[i]<=mx)
printf("1 ");
else
printf("0 ");
}
return 0;
}