题目链接: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\n",ans[tem]);
	}
	return 0;
}

  

01-07 07:13