题目链接:http://codeforces.com/contest/686/problem/D
题意:
给你n个点,以1为根,问你每一颗子树的重心是哪一个节点。
解题思路:
很明显的树形DP,每棵树的重心一定在每个子树的重心到该点的路径上,但是如果考虑这些路径所有的点可能会超时,这是我们可以想到:如果该树的子树的结点数大于该树的两倍那么就不用往上找了。
#include<bits/stdc++.h> using namespace std; const int maxn=300005; const int inf=0x3f3f3f3f; struct st{ int to,next; }stm[maxn*2]; int head[maxn]; int cnt; void add(int u,int v){ stm[cnt].to=v; stm[cnt].next=head[u]; head[u]=cnt++; } int ans[maxn]; int dp[maxn];//最大子树大小 int dp1[maxn];//当前树大小 int pre[maxn]; void dfs(int now,int fa){ dp1[now]=1; dp[now]=0; ans[now]=now; for(int i=head[now];~i;i=stm[i].next){ int to=stm[i].to; if(to==fa)continue; pre[to]=now; dfs(to,now); dp1[now]+=dp1[to]; if(dp[now]<=dp1[to]){ dp[now]=dp1[to]; } } int tem=dp1[now]; for(int i=head[now];~i;i=stm[i].next){ int pos=stm[i].to; if(pos==fa)continue; int tem1=ans[pos]; while(dp1[tem1]*2<=dp1[now]){ if(tem>=max(dp[tem1],dp1[now]-dp1[tem1])){ ans[now]=tem1; tem=max(dp[tem1],dp1[now]-dp1[tem1]); } tem1=pre[tem1]; } if(tem>=max(dp[tem1],dp1[now]-dp1[tem1])){ ans[now]=tem1; tem=max(dp[tem1],dp1[now]-dp1[tem1]); } } } int main(){ int n,q; int tem; memset(head,-1,sizeof(head)); scanf("%d%d",&n,&q); pre[1]=1; for(int i=2;i<=n;i++){ pre[i]=i; scanf("%d",&tem); add(i,tem); add(tem,i); } dfs(1,0); //for(int i=1;i<=n;i++)cout<<dp1[i]<<endl; for(int i=1;i<=q;i++){ scanf("%d",&tem); printf("%d ",ans[tem]); } return 0; }