AIM Tech Round 3 (Div. 1)C.Centroids
题意:题意:给一棵树,问树的每个点能不能通过仅一次删边加边变成质心,所谓质心即删掉该点和相邻的边剩下的每个联通块大小都小于等于n/2。n ≤ 400000
思路:树形dp+换根dp
$umax[i]表示除了以点i为根的树,整颗大树的剩余部分中,结点数最多的子树的值$
$son[i]表示以点i为根的树的总结点数$
$uson[i]表示除了以点i为根的树,其余的总结点数$
$fi[i],se[i]表示以点i为根的树中,权值最大和次大的两颗子树对应的值$
$flag[i]标记以点i为根的树对应3种情况:为0表示所连子树中没有一个是超过n/2,可以得出该点一定可以为重心。$
$为-1 表示上方子树超过了n/2。为确切数字表示所连子树中超过了n/2$
对于每一个点,将其看为一棵树的根,如果这棵树的所有子树的结点数都小于等于$n/2$时,这个点总是可以成为重心的
如果这棵树的子树中有一棵子树 $ t $ 的结点数大于$n/2$,那么我们需要去判断如果把子树 $t$ 的$fi[t]$删去,连接到点 $i$ 是否可行
如果除了以点 $i$ 为根的树,整颗大树的剩余部分中,结点数最多的子树$t$的值大于 $n/2$ ,
同样判断如果把子树 $t$ 的$fi[t]$删去,连接到点 $i$ 是否可行。
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int N =4e5+7,M=8e5+7;
int h[N],e[M],ne[M],idx;
void add(int a,int b){
e[idx]=b;ne[idx]=h[a];h[a]=idx++;
}
int n,umax[N],son[N],uson[N],fi[N],se[N],flag[N],ans[N];
void dfs1(int u,int fa){
son[u]=1;uson[u]=1;
umax[u]=1;
for(int i=h[u];i!=-1;i=ne[i]){
int v=e[i];
if(v==fa)continue;
dfs1(v,u);
son[u]+=son[v];
if(son[v]>n/2)flag[u]=v;
if(son[v]<=n/2){
if(son[v]>fi[u])se[u]=fi[u],fi[u]=son[v];
else if(son[v]>se[u])se[u]=son[v];
}
else {
if(fi[v]>fi[u])se[u]=fi[u],fi[u]=fi[v];
else if(fi[v]>se[u])se[u]=fi[v];
}
}
uson[u]=n-son[u];
if(uson[u]>n/2)flag[u]=-1;
}
void dfs2(int u,int fa){
if(uson[u]<=n/2){
umax[u]=uson[u];
}
else if(fi[fa]==son[u]){
umax[u]=max(umax[fa],se[fa]);
}
else {
umax[u]=max(umax[fa],fi[fa]);
}
for(int i=h[u];~i;i=ne[i]){
int v=e[i];
if(v==fa)continue;
dfs2(v,u);
}
}
int main(){
cin>>n;
memset(h,-1,sizeof h);
memset(umax,1,sizeof umax);
for(int i=1;i<n;i++){
umax[i]=1;
int a,b;cin>>a>>b;
add(a,b);
add(b,a);
}
dfs1(1,-1);
dfs2(1,-1);
for(int i=1;i<=n;i++){
if(flag[i]==0)cout<<"1 ";
else if(flag[i]==-1&&uson[i]-umax[i]<=n/2)cout<<"1 ";
else if(flag[i]!=-1&&son[flag[i]]-fi[flag[i]]<=n/2)cout<<"1 ";
else cout<<"0 ";
}
cout<<endl;
}