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;
}