


























Treap 是一种结合了 二叉搜索树(Binary Search Tree) 和 二叉堆(Binary Treap) 特性的高级平衡数据结构,其名称正是由这两个词组合而成:Tree + Heap \(Rightarrow\) Treap。
更具体地说,Treap 中的每个节点都存储了一个二元组 \((X,Y)\):
Treap 在几何学和特定算法语境下也被称为 笛卡尔树(Cartesian Tree),因为它可以非常直观地嵌入到直角坐标系中,该结构由 Raimund Seidel 和 Cecilia Aragon 于 1989 年提出。
在普通的二叉搜索树(BST)中,如果输入的数据是有序的(例如递增序列),BST 会退化成一条极其低效的 单链。此时,树的深度变为 \(O(N)\),基本的增删改查操作复杂度也会全面退化到 \(O(N)\)。
Treap 引入了 优先级(Priority) \(Y\) 来解决这一痛点:
在算法竞赛实现中,通常使用“非旋转式(无旋)Treap”。它不依赖传统的树旋转,而是通过“分裂”和“合并”这两个核心辅助操作来完成所有复杂的增删改工作。
有了这两个基础部件,插入和删除将变得非常简洁。
为了让 Treap 支持更多实用功能(如:在 \(O(\log N)\) 时间内 寻找第 \(K\) 大的元素,或者查询某个元素在有序列表中的 排名/索引),通常需要在节点中增加一个字段 \(s\),用来存储 以该节点为根的子树中的总节点数量。
当树的结构因插入、删除、分裂或合并而发生改变时,受影响节点的 \(s\) 值必须及时更新。
动态维护一个可重集合 \(M\),进行 \(n \ (1 \le n \le 10^5)\) 次操作,需要实现以下 6 种操作:
- 插入数 \(x \ (|x| \le 10^7)\)。
- 删除数 \(x\)(若有多个相同的数,仅删除一个)
- 查询数 \(x\) 的排名(即定义为:小于 \(x\) 的数的个数 \(+1\))
- 查询排名为 \(x\) 的数
- 求 \(x\) 的前驱(定义为小于 \(x\) 且最大的数)
- 求 \(x\) 的后继(定义为大于 \(x\) 且最小的数)
#include <cstdio>
#include <random>
#include <chrono>
using namespace std;
const int N = 100005;
int root, val[N], l[N], r[N], sz[N], tot, pri[N];
mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());
void update(int p) {
sz[p] = sz[l[p]] + sz[r[p]] + 1;
}
void split(int p, int v, int& a, int& b) {
if (!p) {
a = b = 0;
return;
}
if (val[p] <= v) {
a = p;
split(r[p], v, r[a], b);
} else {
b = p;
split(l[p], v, a, l[b]);
}
update(p);
}
int create(int v) {
val[++tot] = v;
pri[tot] = rnd();
sz[tot] = 1;
l[tot] = r[tot] = 0;
return tot;
}
int merge(int a, int b) {
if (!a || !b) return a + b;
if (pri[a] > pri[b]) {
r[a] = merge(r[a], b);
update(a);
return a;
} else {
l[b] = merge(a, l[b]);
update(b);
return b;
}
}
int kth(int p, int k) {
while (p) {
if (k <= sz[l[p]]) p = l[p];
else if (k == sz[l[p]] + 1) return val[p];
else {
k -= sz[l[p]] + 1;
p = r[p];
}
}
return 0;
}
int main()
{
int n; scanf("%d", &n);
while (n--) {
int op, x; scanf("%d%d", &op, &x);
if (op == 1) {
int a, b;
split(root, x, a, b);
root = merge(merge(a, create(x)), b);
} else if (op == 2) {
int a, b, c;
split(root, x, a, c);
split(a, x - 1, a, b);
if (b) b = merge(l[b], r[b]);
root = merge(merge(a, b), c);
} else if (op == 3) {
int a, b;
split(root, x - 1, a, b);
printf("%d\n", sz[a] + 1);
root = merge(a, b);
} else if (op == 4) {
printf("%d\n", kth(root, x));
} else if (op == 5) {
int a, b;
split(root, x - 1, a, b);
printf("%d\n", kth(a, sz[a]));
root = merge(a, b);
} else {
int a, b;
split(root, x, a, b);
printf("%d\n", kth(b, 1));
root = merge(a, b);
}
}
return 0;
}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。