CF685B Kay and Snowflake

CF685B Kay and Snowflake

题意

题目大意

给定一棵以 \(1\) 为根、包含 \(n\) 个节点的树,进行 \(q\) 次查询。每次查询给出一个节点 \(v_i\),要求找出以 \(v_i\) 为根的子树的重心。重心的定义是:删除该节点后,剩余所有连通块的大小均不超过原子树大小的一半。

数据范围

  • 节点数 \(n\) 和查询次数 \(q\)\(2 \leq n, q \leq 3 \times 10^5\)
  • 树的输入方式:第 \(i\) 个节点(\(2 \leq i \leq n\))的父节点 \(p_i\) 给出,保证构成合法树结构
  • 保证每个查询的子树至少存在一个重心

题解

观察到,如果 \(u\)\(v\) 的父亲,那么 \(u\) 子树的重心的深度一定不大于 \(v\) 子树的重心的深度。

据此贪心即可,对于每个节点,取所有儿子中的最优解,因为每个点只会被访问一次,所以时间复杂度 \(O(n)\).

代码

#include<bits/stdc++.h>
using namespace std;
const int Maxn=3e5+10;
int n,q;
int mx[Maxn],ans[Maxn];
vector<int>edge[Maxn];
int sz[Maxn],fa[Maxn];
void dfs(int u)
{
	sz[u]=1;
	for(int i=0;i<edge[u].size();i++)
	{
		int v=edge[u][i];
		if(v==fa[u]) continue;
		dfs(v);
		sz[u]+=sz[v];
		mx[u]=max(mx[u],sz[v]);
	}
    ans[u]=u;
	for(int i=0;i<edge[u].size();i++)
	{
		int v=edge[u][i];
		if(v==fa[u]) continue;
		v=ans[v];
		while(fa[v]!=u && max(mx[v],sz[u]-sz[v])>=max(mx[fa[v]],sz[u]-sz[fa[v]])) v=fa[v];
		if(max(mx[v],sz[u]-sz[v])<max(mx[ans[u]],sz[u]-sz[ans[u]])) ans[u]=v;
	}
}
int main()
{
	cin>>n>>q;
	for(int i=2;i<=n;i++)
	{
		int u;
		cin>>u; fa[i]=u;
		edge[u].push_back(i);
		edge[i].push_back(u);
	}
	dfs(1);
    while(q--)
    {
        int u;
        cin>>u;
        cout<<ans[u]<<endl;
    }
}
posted @ 2025-05-14 18:52  crazy--boy  阅读(43)  评论(0)    收藏  举报