#include<bits/stdc++.h>#define int long longusingnamespacestd;constintmaxn=2e5+5;constintINF=0x3f3f3f3f;intn;intdp_ch[maxn],dp_fa[maxn],sec[maxn],ans[maxn];vector<int>G[maxn];voiddfs_ch(intu,intpar){for(autov:G[u]){if(v==par)continue;dfs_ch(v,u);if(dp_ch[v]+1>dp_ch[u]){sec[u]=dp_ch[u];dp_ch[u]=dp_ch[v]+1;}elseif(dp_ch[v]+1>sec[u]){sec[u]=dp_ch[v]+1;}}}voiddfs_fa(intu,intpar){for(autov:G[u]){if(v==par)continue;if(dp_ch[v]+1==dp_ch[u]){dp_fa[v]=max(sec[u],dp_fa[u])+1;dfs_fa(v,u);}else{dp_fa[v]=max(dp_ch[u],dp_fa[u])+1;dfs_fa(v,u);}}}signedmain(){ios::sync_with_stdio(0);cin.tie(0);cin>>n;for(inti=0,u,v;i<n-1;i++){cin>>u>>v;G[u].push_back(v);G[v].push_back(u);}dfs_ch(1,0);dfs_fa(1,0);for(inti=1;i<=n;i++){cout<<max(dp_ch[i],dp_fa[i])<<" ";}}
voiddiameter_fa(intu,intpar){for(intv:adj[u]){if(v==par)continue;// 斷邊 (u, v),u 以上含 u 的連通塊的答案// 清除 v 在 ans(u) 的貢獻erase_one(ch_dp[u],dp_ch[v]);erase_one(ch_hei[u],hei[v]+1);// 計算答案 max (樹直徑, 往上或往下的最大高度+往上或往下的次小高度)dp_fa[v]=mav(*rbegin(ch_dp[u]),*rbegin(ch_hei[u])+*nevt(rbegin(ch_hei[u])));ch_dp[v].insert(dp_fa[v]);// 由 v -> u -> ... 往上的最大高度ch_hei[v].insert(*rbegin(ch_hei[u])+1);// 加回 v 在 ans(u) 的貢獻ch_dp[u].insert(dp_ch[v]);ch_hei[u].insert(hei[v]+1);diameter_fa(v,u);}}
針對節點 u,維護兩個值:一個是從其子樹中尋找且不超過 的最大子樹大小;另一個是將以節點 u 為根的子樹移除後,剩下的樹中能找到的最大且不超過 的子樹大小。我們令第一種是 dp_ch(u),第二種是 dp_fa(u)。
第一種情況轉移式顯然,為
第二種情況假設目前是從 u 為根轉移到 v 為根,可能 u 的整塊的子樹都 <= n/2,那就 dp_fa(v) 就直接是 n - sz(v),否則就是 u 的小孩裡面,除了 v 以外 dp_ch(v) 最大的,這個可以用前綴最大值與後綴最大值算出來,或者是 u 的 father 的連通塊的答案,也就是 dp_fa(u)。
最後對於節點 u,找到以 u 為根時有最大子樹大小的兒子,減去 max{ dp_ch(u), dp_fa(u) } 看是否小於 n / 2 就可以判斷了。