Kay and Snowflake CodeForces - 686D
阿新 • • 發佈:2021-07-17
原題連結
考察:樹形dp(?)
思路:
主要利用的兩個性質:
(1) 以樹的重心為根時,所有子樹的大小都不超過整棵樹大小的一半.
(2) 把兩棵樹通過一條邊相連得到一棵新的樹,那麼新的樹的重心在連線原來兩棵樹的重心的路徑上.
注意性質1不是子樹的和,以貪心來考慮的話,我們對於根節點\(u\),其最大的子樹根為\(v\),那麼\(sz[v]*2<=sz[u]\),則說明\(u\)是重心.
因此,對於每個點我們記錄最大的子節點\(son[u]\),子節點個數\(sz[u]\).
Code
#include <iostream> #include <cstring> using namespace std; const int N = 300010; int h[N],idx,f[N],n,Q,sz[N],fa[N],ans[N],son[N]; struct Road{ int to,ne; }road[N<<1]; void add(int a,int b) { road[idx].to = b,road[idx].ne = h[a],h[a] = idx++; } int dfs(int u) { int maxn = 0,tar = 0; sz[u] = 1; for(int i=h[u];~i;i=road[i].ne) { int v = road[i].to; sz[u]+=dfs(v); if(sz[v]>sz[son[u]]) son[u] = v; } if(sz[son[u]]*2<=sz[u]) ans[u] = u; else{ int t = ans[son[u]]; while(fa[t]&&max(sz[son[t]],sz[u]-sz[t])*2>sz[u]) t = fa[t]; ans[u] = t; } return sz[u]; } int main() { scanf("%d%d",&n,&Q); for(int i=1;i<=n;i++) h[i] = -1,sz[i] = 1; for(int i=2,j;i<=n;i++) { scanf("%d",&j); add(j,i); fa[i] = j; } dfs(1); while(Q--) { int x; scanf("%d",&x); printf("%d\n",ans[x]); } return 0; }