思路
- 从下往上处理每个子树的重心。
- 对于任意点 \(u\),其所在子树的中心一定在 \(u\) 和 \(ans[to]\) 之间,\(ans[to]\) 是重儿子 \(to\) 的重心结点。
- 对于任意一点 \(u\),其所在子树的重心深度一定不大于 \(ans[to]\)。
代码
假设一个结点 \(u\) 的子树大小为 \(sz[u]\)。
对于 \(u\),我们从它的重儿子结点的答案结点开始向上跳,直到跳到的点 \(p\) 的子树大小 \(sz[p]\) 的两倍大于 \(u\) 的子树大小,这时 \(sz[u] - sz[p] < \dfrac{sz[u]}{2}\), \(u\) 的每一个子树大小也不可能超过 \(\dfrac{sz[u]}{2}\)。
所以 \(p\) 就是 \(u\) 的重心结点。
#include <bits/stdc++.h>
using namespace std;
const int N = 300010;
struct edge {
int to, next;
} e[N];
int head[N], idx = 1;
void add(int u, int v) {
idx++, e[idx].to = v, e[idx].next = head[u], head[u] = idx;
}
int n, q, sz[N], son[N], ans[N], fa[N];
void dfs(int u) {
sz[u] = 1; // 子树大小
for (int i = head[u]; i; i = e[i].next) {
int to = e[i].to;
fa[to] = u; // 父亲结点
dfs(to);
sz[u] += sz[to];
if (sz[son[u]] < sz[to]) son[u] = to;// 重儿子
}
if (!head[u]) { // 叶子结点
ans[u] = u;
return;
}
int p = ans[son[u]];
while (sz[p] * 2 <= sz[u]) p = fa[p];
ans[u] = p;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> q;
for (int i = 2; i <= n; i++) {
int fa;
cin >> fa;
add(fa, i);
}
dfs(1);
while (q--) {
int x;
cin >> x;
cout << ans[x] << '\n';
}
return 0;
}
标签:sz,结点,子树,int,head,CF685B,ans,Kay,Snowflake
From: https://www.cnblogs.com/Yuan-Jiawei/p/18348349