可并堆(左偏树)小记

posted on 2025-02-16 12:22:08 | under | source

维护一个堆,支持合并操作。

需要满足左偏性质:\(dist_{lson}\ge dist_{rson}\)\(dist_i\)\(i\) 与关键点(左儿子或右儿子为空)最小距离,有 \(dist_u=dist_{rson}+1\)

那么有关键性质:\(dist_u=O(\log siz_u)\)。证明:若已知 \(dist_u\) 那么要令节点尽量少,则 \(dist_{lson}=dist_{rson}\),发现是个完全二叉树,那么就是 \(\log\) 级别的。

注意一下说的是 \(dist\),而深度可能很大。

合并 \(x,y\):设 \(val_x\le val_y\),那么让 \(rson_x\)\(y\) 合并,结束后更新一下即可。

复杂度是 \(O(dist_x+dist_y)\) 的,即为 \(O(\log n)\)

删去根:合并左右儿子即可。

查询一个点所在堆的根:用并查集维护即可。

模板题代码

#include<bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;
int n, T, opt, x, y;
bool vis[N];  

namespace ZPS{
	int tot, rot;
	struct node{int ls, rs, fa, d, val;} t[N];
	
	inline int newnode(int x) {++tot; t[tot] = {0, 0, tot, 0, x}; return tot;}
	inline void psup(int u){
		if(t[t[u].ls].d < t[t[u].rs].d) swap(t[u].ls, t[u].rs);
		t[u].d = t[t[u].rs].d + 1;
	}
	inline int mer(int x, int y){
		if(!x || !y) return x + y;
		if((t[x].val == t[y].val && x > y) || t[x].val > t[y].val) swap(x, y);
		t[x].rs = mer(t[x].rs, y);
		psup(x);
		return x;
	}
	inline void Mer(int x, int y) {int p = mer(x, y); t[x].fa = t[y].fa = p;}
	inline int find(int x) {return t[x].fa == x ? x : t[x].fa = find(t[x].fa);}
	inline void del(int x) {vis[x] = true; int p = mer(t[x].ls, t[x].rs); t[x].fa = t[p].fa = p;}
}using namespace ZPS;

signed main(){
	t[0].d = -1;
	
	cin >> n >> T;
	for(int i = 1; i <= n; ++i) scanf("%d", &x), x = newnode(x);
	while(T--){
		scanf("%d%d", &opt, &x);
		if(opt == 1){
			scanf("%d", &y);
			if(vis[x] || vis[y]) continue;
			int fx = find(x), fy = find(y);
			if(fx ^ fy) Mer(fx, fy);
		}
		else{
			if(vis[x]) {puts("-1"); continue;}
			int fx = find(x);
			printf("%d\n", t[fx].val);
			del(fx), vis[fx] = true;
		}
	}
	return 0;
}
posted @ 2026-01-14 18:07  Zwi  阅读(2)  评论(0)    收藏  举报