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

浙公网安备 33010602011771号