分尸(树)
重链剖分
思路
考虑将一个节点的儿子中所在子树最大的一个儿子作为重儿子,相连的边为重边,每次延重儿子划分划出来的链我们称之为重链。
对于一条链如果往上跳,必然是个轻边,所以必然存在一个重儿子的字数大于当前链所在子树,所以没往上条链,子树大小至少为原来两倍,所以时间复杂度为 \(logn\)。
code
void DFS(int x, int fa) {
sz[x] = 1;
for (int i : g[x]) {
if (i == fa) continue;
dep[i] = dep[x] + 1, DFS(i, x), sz[x] += sz[i], gfa[i] = x, (sz[son[x]] < sz[i]) && (son[x] = i);
}
}
void S(int x, int fa, int to) {
top[x] = to, dfn[x] = ++cnt, mxdfn[x] = dfn[x];
if (son[x]) S(son[x], x, to), mxdfn[x] = max(mxdfn[x], mxdfn[son[x]]);
for (int i : g[x]) {
if (i == fa || i == son[x]) continue;
S(i, x, i), mxdfn[x] = max(mxdfn[i], mxdfn[x]);
}
}
重链剖分求重心
重心是重链上深度最大的满足sz[x]*2>sz[rt]的x,所以可以二分,这里提供一个DFS的重链剖法
#include <iostream>
#include <vector>
using namespace std;
const int MaxN = 3e5 + 10;
int son[MaxN], dfn[MaxN], maxdfn[MaxN], sz[MaxN], p[MaxN], top[MaxN], gfa[MaxN], dep[MaxN], tot, n, q;
vector<int> g[MaxN];
void DFS(int x, int fa = 0, int to = 1) {
sz[x] = 1, gfa[x] = fa, dep[x] = dep[fa] + 1, maxdfn[x] = dfn[x] = ++tot - n, (dfn[x] > 0) && (p[dfn[x]] = x), top[x] = to;
if (!son[x]) {
for (int i : g[x]) {
if (i == fa) continue;
DFS(i, x), sz[x] += sz[i], (sz[son[x]] < sz[i]) && (son[x] = i);
}
return void((x == 1) && (DFS(1), 0));
}
DFS(son[x], x, to), sz[x] += sz[son[x]], maxdfn[x] = maxdfn[son[x]];
for (int i : g[x]) {
if (i == fa || i == son[x]) continue;
DFS(i, x, i), sz[x] += sz[i];
}
}
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> q;
for (int i = 2, fa; i <= n; i++) {
cin >> fa;
g[fa].push_back(i);
g[i].push_back(fa);
}
DFS(1);
for (int k; q; q--) {
cin >> k;
int l = dfn[k], r = maxdfn[k];
while (l < r) {
int mid = l + r + 1 >> 1;
(sz[p[mid]] * 2 > sz[k] ? l = mid : r = mid - 1);
}
cout << p[l] << '\n';
}
return 0;
}

浙公网安备 33010602011771号