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

浙公网安备 33010602011771号