AIM Tech Round 3 (Div. 1)]C.Centroids
阿新 • • 發佈:2021-12-12
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; }