平衡树
学太多平衡树也没有用
所以这里侧重点不一样
来介绍一些平衡树
FHQ Treap
无旋 \(Treap\)
通过合并与分裂来维护堆的性质, 代码都比较容易理解
- 一些基本操作, 可以开个栈来实现空间回收
struct Tree {
int l, r;
int dat, val;
int cnt, size;
} a[N];
int n, tot, root;
int New(int val) {
a[++tot].val = val;
a[tot].dat = std::rand();
a[tot].size = 1;
return tot;
}
void updata(int pos) {
a[pos].size = a[a[pos].l].size + a[a[pos].r].size + 1;
}
- 分裂
void split(int p, int &x, int &y, int k) {
if (p == 0) {
x = y = 0;
return ;
}
if (a[p].val <= k) {
x = p;
split(a[p].r, a[p].r, y, k);
} else {
y = p;
split(a[p].l, x, a[p].l, k);
}
updata(p);
}
- 合并
int merge(int A, int B) {
if (!A || !B)
return A + B;
if (a[A].dat < a[B].dat) {
a[A].r = merge(a[A].r, B);
updata(A);
return A;
} else {
a[B].l = merge(A, a[B].l);
updata(B);
return B;
}
}
- 第 \(k\) 大
int kth(int p, int k) {
while (true) {
if (k <= a[a[p].l].size)
p = a[p].l;
else if (k == a[a[p].l].size + 1)
return p;
else
k -= a[a[p].l].size + 1, p = a[p].r;
}
}
- 插入与删除
void insert (int &root, int k) {
split(root, x, y, k);
root = merge(merge(x, New(k)), y);
}
void remove (int &root, int k) {
split(root, x, z, k);
split(x, x, y, k - 1);
if (y) sta.push(y);
y = merge(a[y].l, a[y].r);
root = merge(merge(x, y), z);
}
查询前驱后继可以通过 分裂与 \(kth\) 实现
一些重要的东西来了
- 无交合并
看代码也可以比较容易的理解
int merge (int A, int B) {
int C = 0;
while (A && B) {
if (a[A].Min > a[B].Min) std::swap(A, B);
int left = 0;
split (A, left, A, a[B].Min);
C = Merge (C, left);
}
C = Merge (C, A + B);
return C;
}
算是一个科技了, 复杂度是 \(log^2\) 的
不会分析, 可以用来做函数复合问题
- 区间操作
平衡树分裂的时候按照 \(kth\) 的方法来分裂
合并的时候按顺序合并
\([1, l - 1], [l, r], [r + 1, n]\)
分成这三个区间, 中间整体打标记, 然后就可以做了
容易理解且好写
- 可持久化
可持久化需要把每个历史版本都记录下来, 但是我们发现修改的地方很少, 所以有一些东西是重复的, 可以直接继承, FHQ_Treap 的简单操作, 可以让他很愉快的进行可持久化
只需在合并与分裂的时候, 不去继承原先的节点, 而是去新开一个节点就可以了
空间可能需要的多一点
注意翻转 \(push\) 的时候需要再次新建节点
因为对他进行了修改

浙公网安备 33010602011771号