分尸(树)

重链剖分

思路

考虑将一个节点的儿子中所在子树最大的一个儿子作为重儿子,相连的边为重边,每次延重儿子划分划出来的链我们称之为重链。

对于一条链如果往上跳,必然是个轻边,所以必然存在一个重儿子的字数大于当前链所在子树,所以没往上条链,子树大小至少为原来两倍,所以时间复杂度为 \(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;
} 
posted @ 2024-08-21 21:33  yabnto  阅读(26)  评论(0)    收藏  举报