全局平衡二叉树

一种常数较小的能在单次 \(O(\log n)\) 时间内解决链修改链查询的数据结构。

普通的 LCT 也是 \(O(\log n)\) 的,但是常数巨大。原因是它用辅助树维护了一个动态的虚实链剖分,在没有动态加边删边的问题中这显然是没有必要的。我们考虑将 LCT 强行静态化来减小长度。

具体的,我们仍用类似 LCT 的结构来维护树的一个轻重链剖分。对于一条重链,我们不用 splay 而是用一棵静态的二叉搜索树。每次将点权设为轻子树的 \(siz\) 和,求出链的带权重心,往两边递归建树。建树的代码:

inline int build(int now) {
	vector<int> s;
	for (int i = now; i; i = zson[i]) vis[i] = 1;
	for (int i = now; i; i = zson[i]) {
		for (int j = ed[i].head; j; j = ed[j].nxt) {
			int v = ed[j].to;
			if (vis[v]) continue;
			fa[build(v)] = i;
		} s.push_back(i); lsiz[i] = siz[i] - siz[zson[i]];
	}
	auto calc = [&s](auto &f, int l, int r) {
		if (l > r) return 0;
		ll sum = 0, cnt = 0;
		for (int i = l; i <= r; ++i) sum += lsiz[s[i]];
		for (int i = l; i <= r; ++i) {
			cnt += lsiz[s[i]];
			if (2 * cnt >= sum) {
				rs(s[i]) = f(f, l, i - 1);
				ls(s[i]) = f(f, i + 1, r);
				if (ls(s[i])) fa[ls(s[i])] = s[i];
				if (rs(s[i])) fa[rs(s[i])] = s[i];
				update(s[i]);
				return s[i];
			}
		}
		return 0;
	}; int rt = calc(calc, 0, s.size() - 1);
	return rt;
}

链修改是好做的,但如果要维护子树信息就要维护考虑轻儿子的标记,比较麻烦,不如树剖(

posted @ 2023-11-07 20:31  Smallbasic  阅读(23)  评论(0)    收藏  举报