可并堆(左偏树)小记
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;
}

浙公网安备 33010602011771号