左偏树学习笔记

左偏树 (Leftist Heap) 是一种可并堆,他支持在 \(O(\log n)\) 的时间内将两个堆合并。

具体来说,左偏树除了有堆的性质(节点小于儿子),还额外维护一个值 \(dist\),表示当前节点最少经过多少条边可以到一个叶子节点。这里定义叶子节点的 \(dist=0\),空节点 \(dist=-1\)

\(dist\) 的性质是:

  • 对于一个节点 \(x\)\(dist(x)=dist(rson)+1\)
  • 任意一棵左偏树的 \(dist\) 不会超过 \(\log N\),其中 \(N\) 是左偏树节点个数。

证明:

  • \(dist\) 的定义可知 \(dist(x)=\min(dist(lson), dist(rson))+1\),又因为 \(dist(lson)\ge dist(rson)\),故 \(dist(x)=dist(rson)+1\)
  • 考虑一棵 \(dist=k\) 的左偏树最少有多少个节点,不放令这个值为 \(S(k)\)。根据第一条性质,我们知道 \(dist(x)=dist(rson)+1\),故其右儿子至少有 \(S(k-1)\) 个节点,左儿子也应该至少有 \(S(k-1)\) 个节点,故总结点个数 \(S(k)=2\times S(k-1)+1\)。由于边界情况 \(S(0)=1\),故 \(S(k)=2^{k+1}-1\)。即 \(k\le \log_2(N+1)-1\)

每次合并两个堆的时候,设他们的根节点为 \(x,y\)\(val(x)<val(y)\),我们递归的把 \(x\) 的右儿子与 \(y\) 合并,然后再把这个合并后的堆当做 \(x\) 的右儿子。如果此时 \(dist(rson)>dist(lson)\),那么就交换左右儿子。

时间复杂度证明:

  • 每次合并后,得到的一个新的堆仍然满足左偏树的性质。
  • 递归的次数等于右儿子的个数,也就是 \(dist(x)\)

故时间复杂度为 \(O(\log n)\)

模板题:LG3377. 【模板】左偏树/可并堆

const int MAXN = 1e5 + 5;
struct Node {
	int val, dist, idx;
	Node *ls, *rs;
	
	bool operator < (const Node b) const {
		return val == b.val ? idx < b.idx : val < b.val;
	}
};
Node *rt[MAXN];
int n, m, fa[MAXN], del[MAXN];

int find(int x) {
	if (fa[x] == x) return x;
	else return fa[x] = find(fa[x]);
}

int dist(Node *x) {
	if (x == nullptr) return -1;
	else return x->dist;
}

Node* merge(Node* x, Node* y) {
	if (x == nullptr) return y;
	if (y == nullptr) return x;
	
	if (*y < *x) swap(x, y);
	x->rs = merge(x->rs, y);
	
	if (dist(x->ls) < dist(x->rs)) swap(x->ls, x->rs);
	x->dist = dist(x->rs) + 1;
	
	return x;
}

void work() {
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) {
		int x; cin >> x;
		rt[i] = new Node({x, 0, i, nullptr, nullptr});
		fa[i] = i;
	}
	while (m--) {
		int op; cin >> op;
		if (op == 1) {
			int x, y; cin >> x >> y;
			if (del[x] || del[y]) continue;
			if (find(x) == find(y)) continue;
			rt[find(x)] = merge(rt[find(x)], rt[find(y)]);
			fa[find(y)] = find(x);
		} else {
			int x; cin >> x;
			if (del[x]) {
				cout << -1 << endl;
				continue;
			}
			cout << rt[find(x)]->val << endl;
			del[rt[find(x)]->idx] = true;
			rt[find(x)] = merge(rt[find(x)]->ls, rt[find(x)]->rs);
		}
	}
}
posted @ 2025-10-11 10:37  小蛐蛐awa  阅读(10)  评论(0)    收藏  举报