CF685B Solution

题目链接

题解

若树的重心为根节点,所有子树大小均小于树大小的\(\frac{1}{2}\)。易得,当树的重心并非根节点时,其一定在树的最大子树中。设最大子树重心为\(cen\)\(cen\)的子树一定小于最大子树的\(\frac{1}{2}\),也小于整棵树的\(\frac{1}{2}\)。因此我们只需不断上移\(cen\),因为树的重心一定存在,所以第一个满足全树除\(cen\)的子树外的部分也小于全树的\(\frac{1}{2}\)的节点一定为重心。

上述过程可用递归实现,记录所有节点子树的重心后离线回答询问即可。因为所有\(cen\)只升不降,时间复杂度约为\(O(n)\)

AC代码

#include<iostream>
using namespace std;
const int N=3e5+10;
int fst[N],nxt[N*2],v[N*2],cnt;
int cen[N],siz[N],fa[N];//cen[i]:节点i子树的重心,siz[i]:节点i子树的大小
void add(int x,int y)
{
	v[++cnt]=y;
	nxt[cnt]=fst[x]; fst[x]=cnt;
} 
void dfs(int x)
{
	siz[x]=1,cen[x]=x;//初始x子树的重心为x
	for(int i=fst[x];i;i=nxt[i])
	{
		int y=v[i]; 
		if(y!=fa[x]) 
		{
			fa[y]=x; dfs(y); 
			siz[x]+=siz[y];
		}
	}
	for(int i=fst[x];i;i=nxt[i])
	{
		int y=v[i];
		if(y!=fa[x] && siz[y]*2>siz[x]) cen[x]=cen[y];//记录大于全树一半子树的重心
	}
	while((siz[x]-siz[cen[x]])*2>siz[x]) cen[x]=fa[cen[x]];//上移
}
int main()
{
	int n,q,x;
	scanf("%d%d",&n,&q);
	for(int i=2;i<=n;i++)
	{
		scanf("%d",&x);
		add(x,i),add(i,x);
	}
	dfs(1);
	while(q--) {scanf("%d",&x); printf("%d\n",cen[x]);}
	return 0;
}
posted @ 2021-02-08 12:16  violet_holmes  阅读(46)  评论(0编辑  收藏  举报