左偏树
左偏树是一种可并堆(一系列的堆),支持以下操作:
-
删除一个堆的最值。
-
查询一个堆的最值。
-
新建一个堆,只包含一个元素。
-
合并两个堆。这个复杂度是
的。
左偏树是一颗二叉树。定义 “外结点” 为儿子数量不等于
左偏树满足对于每个节点,左儿子的
左偏树满足堆的性质,即一个结点是它子树内的最值。
对于 2 操作,输出一个左偏树的根即可。
对于 3 操作,显然很简单。
如果我们实现了 4,则 1 就只需要合并根的左右儿子,然后删除根即可。
如何实现 4 ?我们观察到一个性质。如果左偏树从根出发一直向右走,最多走
证明:假设还能继续往下走。注意到
根据这个结论,可以设计出
-
初始有两颗左偏树
,不妨 的根优于 。 -
合并
右节点的子树 和 ,将合并出来的左偏树作为 的右儿子。 -
如果此时
右儿子的 大于左儿子,交换左右儿子。
使用并查集快速查询某个结点所在堆的根。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
int n, m;
// 值类型,默认构造函数应保存最不优先的值
struct Val {
int v;
Val() {
v = inf;
}
Val(int x) {
v = x;
}
} ide;
// 定义为大的优先
bool operator<(Val x, Val y) {
return x.v > y.v;
}
struct LefHeap {
int sz;
vector<Val> a; // 值
vector<bool> f; // 是否未删除
vector<int> l, r, d, p; // 左子、右子、深度、并查集父结点
// 查找x号元素所在的根
int fnd(int x) {
if (p[x] == x)
return x;
return p[x] = fnd(p[x]);
}
// 合并x, y号元素为根的堆,返回根编号
int unn(int x, int y) {
if (x == 0 || y == 0) // 一方为空,另一方有效
return x + y;
if (a[x] < a[y]) // 否则,x为根
swap(x, y);
p[y] = x, r[x] = unn(r[x], y);
if (d[l[x]] < d[r[x]])
swap(l[x], r[x]);
d[x] = d[r[x]] + 1; //更新dist
return x;
}
// 加入新元素,值为x
void push(Val x) {
sz++;
l.push_back(0), r.push_back(0), d.push_back(0);
p.push_back(sz), a.push_back(x), f.push_back(true);
}
// 查找x号元素所在堆的顶
Val top(int x) {
return a[fnd(x)];
}
// 删除x号元素所在堆的顶
void pop(int x) {
int rt = fnd(x), new_rt = unn(l[rt], r[rt]);
f[rt] = 0, p[rt] = p[new_rt] = new_rt; // 并查集换根
}
LefHeap() {
sz = 0;
p = l = r = d = vector<int>(1, 0);
a = vector<Val>(1, ide);
f = vector<bool>(1, false);
}
} h;
int main()
{
cin >> n >> m;
for (int i = 1, t; i <= n; i++) {
cin >> t;
h.push(t);
}
for (int i = 1, op, x, y; i <= m; i++) {
cin >> op;
if (op == 1) {
cin >> x >> y;
if (h.f[x] && h.f[y] && h.fnd(x) != h.fnd(y))
h.unn(h.fnd(x), h.fnd(y));
}
else {
cin >> x;
if (h.f[x]) {
cout << h.a[h.fnd(x)].v << endl;
h.pop(x);
}
else
cout << -1 << endl;
}
}
return 0;
}
注:为什么删除的时候还要让 p[rt] = new_rt
?
因为此时我们想要原本所有指向 rt
的都指向 new_rt
,直接让 rt -> new_rt
即可。
注2:只能写 rt -> new_rt
,不能写 l[rt],r[rt] -> new_rt
,因为路径压缩时有的已经直接指向 rt
了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!