左偏树/可合并堆 代码笔记
代码思路
主体部分:
合并堆(即merge函数)
大堆左偏,把小堆和大堆的右儿子合并。
感性理解:堆的形态将比较平衡。
辅助部分:
并查集维护堆关系
简化部分:
自定义数据类型(struct Bheap)
注意事项:
-
堆的最大数量是\(n+m\)
-
注意考虑堆被删空等细节情况(尤其是题目有要求时)
原理(极简)
堆\(+\)左偏 \(\to\) 距离小于\(\log_2{(n+1)}\)
代码注释
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 2000006;
int n, cnt, fa[N]; // 堆的数量
struct Bheap{
int l, r, w, h; // 左儿子 右儿子 堆顶 堆高
}heap[N]; // 堆
bool del[N];
int merge(int a, int b){ // 合并堆
if(heap[a].w < heap[b].w) swap(a, b); // a.w >= b.w
if(!a || !b) return a|b; // 边界情况
// ↑不讨论的话会发生越界导致RE或者MLE
int merg = merge(heap[a].r, b); // 递归
heap[a].r = merg; fa[heap[a].r] = a; // 更新右儿子
if(heap[heap[a].l].h < heap[heap[a].r].h)
swap(heap[a].l, heap[a].r); // 更新左偏
heap[a].h = heap[heap[a].r].h + 1; // 更新堆高
return a;
}
int find(int a){ // 路径压缩的并查集
if(a != fa[a]) fa[a] = find(fa[a]);
return fa[a];
}
int main(){
scanf("%d", &n);
int k, x, y;
for(int i = 1; i <= n; i++){
scanf("%d", &k);
if(k == 1){ // 新增堆
scanf("%d", &x); // x:v
heap[++cnt] = (Bheap){0, 0, x, 1};
fa[cnt] = cnt; del[cnt] = 0; continue;
}
if(k == 2){ // 合并堆
scanf("%d%d", &x, &y);
if(del[x] && del[y]){ printf("-1\n"); continue; }
if(del[x]){ printf("%d\n", heap[find(y)].w); continue; }
if(del[y]){ printf("%d\n", heap[find(x)].w); continue; }
// ↑有堆被删空的情况 ↓合并并输出新堆顶
x = find(x), y = find(y);
if(x != y) fa[x] = fa[y] = merge(x, y);
printf("%d\n", heap[fa[x]].w);
continue;
}
if(k == 3){ // 删除堆顶
scanf("%d", &x);
if(del[x]){ // 如果删空
printf("-1\n");
continue;
}
x = find(x), del[x] = 1; // 删除堆顶
if(heap[x].l){ // 如果堆里还有元素
int l = heap[x].l, r = heap[x].r;
heap[x].l = heap[x].r = 0;
// ↑删除堆顶 ↓合并左右儿子为新堆
fa[x] = fa[l] = fa[r] = merge(l, r);
printf("%d\n", heap[fa[l]].w);
// ↑输出新堆顶
continue;
} // ↓如果删空
printf("-1\n");
}
}
return 0;
}
标签:return,int,合并,fa,heap,左偏
From: https://www.cnblogs.com/meteor2008/p/17869332.html