FHQ Treap
简介
FHQ Treap和普通的Treap都是一个二叉搜索堆,其同时满足二叉树的性质(左子树的权值小于等于当前节点权值,右子树权值大于当前节点权值)和堆的性质(对于小根堆,当前节点的优先级是堆中最小的)。
FHQ Treap与一般的Treap的不同之处主要在于:
- 不用旋转,用split和merge来为维护堆的优先级。
- 能够可持久化。
操作
FHQ Treap的核心操作是split和merge,其他的操作均以这两个操作为基础进行。
下面所述操作的数据如下所示:
struct node {
int val, rnk, lc, rc, size;
}tree[nmax];
val表示节点的权值,用来维护二叉搜索树的性质;rnk表示节点的权值,用来维护堆的性质。lc和rc分别表示节点左子树和右子树的编号,size表示节点的大小(包括本节点在内)。
split
split要实现的是将一颗Treap分为两颗Treap,其中左Treap(以 a a a为根节点)中节点权值均小于等于目标值val,右Treap(以 b b b为根节点)中节点权值均大于目标值val。
给定根 r t rt rt,如果 t r e e [ r t ] . v a l ≤ v a l tree[rt].val \leq val tree[rt].val≤val,那么说明 r t rt rt及其左子树均可以划分给左树 a a a的左子树,接下来只需要考虑左树 a a a的右子树是什么。这时问题可以变为:对于给定的根 t r e e [ r t ] . r c tree[rt].rc tree[rt].rc(因为 r t rt rt的左子树已经划分给了 a a a),按照目标权值 v a l val val将其划分为以 t r e e [ a ] . r c tree[a].rc tree[a].rc和右树 b b b的两颗Treap。问题可以递归求解。
考虑另一种情况,如果 t r e e [ r t ] . v a l > v a l tree[rt].val > val tree[rt].val>val,那么说明 r t rt rt及其右子树均可以划分给右树 b b b的右子树,按照刚才的套路,只需要考虑右树 b b b的左子树是什么。问题进而变为:对于给定的根 t r e e [ r t ] . l c tree[rt].lc tree[rt].lc(因为 r t rt rt的右子树已经划分给了 b b b),按照目标权值 v a l val val将其划分为以 t r e e [ a ] . l c tree[a].lc tree[a].lc和左树 a a a的两颗Treap。问题可以递归求解。
这样split是满足小根堆(大根堆)的性质的。
上述算法的代码如下:
void split(int rt, int & a, int & b, int val) {
if (rt == 0) {
a = b = 0;
return;
}
if (tree[rt].val <= val) {
a = rt;
split(tree[rt].rc, tree[a].rc, b, val);
} else {
b = rt;
split(tree[rt].lc, a, tree[b].lc, val);
}
update(rt);
}
merge
merge要实现的是,将两颗Treap合并为一颗Treap。合并有一个前提,要保证左树 a a a上任意节点的全职都要小于右树 b b b上任意节点权值。这样才能保证合并有序。这里维护的是小根堆。
如果当前 t r e e [ a ] . r n k < t r e e [ b ] . r n k tree[a].rnk < tree[b].rnk tree[a].rnk<tree[b].rnk ,为了同时满足小根堆和二叉搜索树的性质,那么应该将 b b b合并到 a a a的右子树上。
反之,应该将 a a a合并到 b b b的左子树上。递归求解即可,
上述算法的代码如下:
void merge(int & rt, int a, int b) {
// 前提:a的权值全部 < b的权值
if (a == 0 || b == 0) {
rt = a + b;
return;
}
if (tree[a].rnk < tree[b].rnk) {
rt = a;
merge(tree[rt].rc, tree[a].rc, b);
} else {
rt = b;
merge(tree[rt].lc, a, tree[b].lc);
}
update(rt);
}
模板题
//
// Created by pengwill on 2018/9/28.
//
#include <bits/stdc++.h>
using namespace std;
const int nmax = 1e6 + 7;
const int INF = 0x3f3f3f3f;
struct node {
int val, rnk, lc, rc, size;
}tree[nmax];
int tot, seed = 233, root = 0;
inline int rrand() {
return seed = int(seed * 482711ll % 2147483647);
}
inline void update(int rt) {
tree[rt].size = tree[tree[rt].lc].size + tree[tree[rt].rc].size + 1;
}
int add_node(int val) {
tree[++tot].size = 1;
tree[tot].val = val;
tree[tot].lc = tree[tot].rc = 0;
tree[tot].rnk = rrand();
return tot;
}
void split(int rt, int & a, int & b, int val) {
if (rt == 0) {
a = b = 0;
return;
}
if (tree[rt].val <= val) {
a = rt;
split(tree[rt].rc, tree[a].rc, b, val);
} else {
b = rt;
split(tree[rt].lc, a, tree[b].lc, val);
}
update(rt);
}
void merge(int & rt, int a, int b) {
// 前提:a的权值全部 < b的权值
if (a == 0 || b == 0) {
rt = a + b;
return;
}
if (tree[a].rnk < tree[b].rnk) {
rt = a;
merge(tree[rt].rc, tree[a].rc, b);
} else {
rt = b;
merge(tree[rt].lc, a, tree[b].lc);
}
update(rt);
}
void insert(int & rt, int val) {
// 插入一个值为val的节点
int x = 0, y = 0, nnode = add_node(val);
split(rt, x, y, val);
merge(x, x, nnode);
merge(rt, x, y);
}
void delete_node(int & rt, int val) {
// 删除值为val的节点
int x = 0, y = 0, z = 0;
split(rt, x, y, val);
split(x, x, z, val - 1);
merge(z, tree[z].lc, tree[z].rc);
merge(x, x, z);
merge(rt, x, y);
}
int get_kth(int rt, int k) {
// 找第k大的数值
while (tree[tree[rt].lc].size + 1 != k) {
if (tree[tree[rt].lc].size >= k)
rt = tree[rt].lc;
else {
k -= tree[tree[rt].lc].size + 1;
rt = tree[rt].rc;
}
}
return tree[rt].val;
}
int get_rank(int & rt, int val) {
// 查找值对应第几大
int x = 0, y = 0;
split(rt, x, y, val - 1);
int tmp = tree[x].size + 1;
merge(rt, x, y);
return tmp;
}
int get_pre(int & rt, int val) {
// 找值为val的前驱节点数值
int x = 0, y = 0;
split(rt, x, y, val - 1);
int tmp = get_kth(x, tree[x].size);
merge(rt, x, y);
return tmp;
}
int get_scc(int & rt, int val) {
// 找值为val的后继节点数值
int x = 0, y = 0;
split(rt, x, y ,val);
int tmp = get_kth(y, 1);
merge(rt, x, y);
return tmp;
}
int n;
int main() {
scanf("%d", &n);
int op, val;
add_node(INF);
tot = 1;
tree[1].size = 0;
root = 1;
for (int i = 1; i <= n; ++i) {
scanf("%d %d", &op, &val);
if (op == 1) {
insert(root, val);
} else if (op == 2) {
delete_node(root, val);
} else if (op == 3) {
printf("%d\n", get_rank(root, val));
} else if (op == 4) {
printf("%d\n", get_kth(root, val));
} else if (op == 5) {
printf("%d\n", get_pre(root, val));
} else {
printf("%d\n", get_scc(root, val));
}
}
return 0;
}