[CF686D] Kay and Snowflake

题意

给你一棵树,q个询问,每次问你一个子树的重心是谁,如果有多解随便输出一个。(N<=300000,Q<=300000)

 

题解

看数据范围,每次询问只能用O(1)得出答案,简称需要预处理出每个节点的答案。

先设当v为u的子节点且size(v)*2>size(u),v为u的重儿子。

如果u没有重儿子,则u的重心就是u。

如果u有重儿子v,则u的重心一定在v与v的重心的路径上。(我觉得这个思想同样可以运用到CSP2019树的重心)

 

代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<queue>
#include<vector>
#include<cstring>
#include<algorithm>
#include<map>
#include<set>
#include<stack>
#include<sstream>
#include<string>
using namespace std;
#define inf 300010

int fa[inf];
vector<int>edge[inf];
int son[inf];//记录节点数
int ans[inf];//记录每一个子树的重心

void dfs(int u)//遍历找答案 
{
	son[u]=1;//原本只有一个节点 
	ans[u]=u;//开始时每一个节点的重心就是他自己
	int mx=0;
//	printf("u=%d\n",u);
	for(int i=0;i<edge[u].size();i++)//找他后面的节点 
	{
		int v=edge[u][i];
//		printf("v=%d\n",v);
		dfs(v);//向下遍历 
		son[u]+=son[v];
		if(son[v]>son[mx]) mx=v;//找到节点数最大的子树 
	} 
	if(son[mx]*2>son[u])//重子树 
	{
		int v=ans[mx];//从它的重心开始往根节点找
		while((son[u]-son[v])*2>son[u]) v=fa[v];//如果不是重心,就继续往上
		ans[u]=v; 
	}
} 

int main()
{
	int n,q;
	scanf("%d%d",&n,&q);
	for(int i=2;i<=n;i++)
	{
		scanf("%d",&fa[i]);
		edge[fa[i]].push_back(i);//fa[i]通向i节点 
	}
	dfs(1);
	while(q--)
	{
		int x;
		scanf("%d",&x);
		printf("%d\n",ans[x]);
	}
	return 0;
}

  

 

posted @ 2020-12-24 19:11  EDawn  阅读(102)  评论(0)    收藏  举报