原题传送门
题目大意:
给定一棵树,n个节点,给定q个询问,给出n-1个数表示2-n的节点的父节点,每个询问节点给出一个值x,询问以x为根的子树的重心节点
思路浅析:
初始思路是直接跑dfs求出最大子树的最小权值对应的根节点,将其作为一个子树的重心,但是跑着题肯定是会毫无疑问的T飞的,我们需要利用一个树的重心的性质:重心的最大子树不超过整棵树的一半,否则这个点肯定不是重心
code:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 300010;
struct edge
{
int to, nxt;
} e[N << 1];
int cnt;
int x, y;
int n, q;
int head[N], son[N], ans[N], fa[N];
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
f = (ch == '-') ? -1 : 1, ch = getchar();
while (isdigit(ch))
x = x * 10 + (ch - '0'), ch = getchar();
return x * f;
}
inline void add(int x, int y) { e[++cnt].to = y, e[cnt].nxt = head[x], head[x] = cnt; }
inline void dfs(int x, int f) //离线预处理
{
int res = 0; //保证第一次能够合法查询且有一个极小值,那么直接把他设为0
son[x] = 1;
ans[x] = x; //先默认该点是其子树的根节点
for (int i = head[x]; i; i = e[i].nxt) //前向星遍历树
{
if (e[i].to == f)
continue;
dfs(e[i].to, x);
son[x] += son[e[i].to];
if (son[e[i].to] > son[res])
res = e[i].to;
}
if ((son[res] << 1) > son[x]) /* 利用树的重心性质解题 */
/* 以某个点为根,其所有的子树的大小都不差过整个树的一半 */
{
int temp = ans[res];
while (((son[x] - son[temp]) << 1) > son[x])
temp = fa[temp];
ans[x] = temp;
}
}
main()
{
n = read(), q = read();
for (int i = 2; i <= n; ++i)
x = read(), add(i, x), add(x, i), fa[i] = x;
dfs(1, 0);
for (int i = 1; i <= q; ++i)
{
x = read();
std::cout << ans[x], puts("");
}
}