FHQ Treap
无旋Treap
维护的信息
c h [ i ] [ 0 / 1 ] ch[i][0/1] ch[i][0/1] | v a l [ i ] val[i] val[i] | f i x [ i ] fix[i] fix[i] | s i z [ i ] siz[i] siz[i] | t o t tot tot |
---|---|---|---|---|
左右儿子 | 权值 | 随机修正权值 | 以 i i i 为根的子树节点数 | 节点总数 |
r t rt rt | x x x | y y y | z z z |
---|---|---|---|
根节点编号 | 临时子树编号 | 临时子树编号 | 临时子树编号 |
类定义
class FHQ_Treap {
private:
#define ls(k) ch[k][0]
#define rs(k) ch[k][1]
int ch[maxn][2], val[maxn], fix[maxn], siz[maxn], tot;
int rt, x, y, z;
public:
}T;
操作
基本操作
m a i n t a i n ( x ) maintain(x) maintain(x) : 更新节点 x x x 的 s i z siz siz 值
n e w ( v ) new(v) new(v) : 新建一个权值为 v v v 的节点
void maintain(int x) { siz[x] = siz[ls(x)] + siz[rs(x)] + 1; }
int New(int v) {
val[++tot] = v;
fix[tot] = rand();
siz[tot] = 1;
return tot;
}
核心操作
分裂
S p l i t ( i , k , x , y ) Split(i,k,x,y) Split(i,k,x,y) : 当前节点为 i i i ,按权值 k k k 将其分为 x x x , y y y 两个子树, x x x , y y y 为两个子树的根节点编号 (注: x x x 与 y y y 需要加引用,这样分裂操作才可以修改 c h ch ch 来做到正确维护)
- 若当前节点为 0 0 0 ,则说明为空,令 x = y = 0 x = y =0 x=y=0 ,返回
- 当前节点 v a l < = k val <= k val<=k ,则其左子树都可以放在 x x x 上,继续在右子树中寻找
- 当前节点 v a l > k val > k val>k ,则其右子树都可以放在 y y y 上,继续在左子树中寻找
- 寻找完后维护子树大小
void Split(int i, int k, int& x, int& y) {
if(!i) x = y = 0;
else {
if(val[i] <= k) {
x = i;
Split(rs(i), k, rs(i), y);
}
else {
y = i;
Split(ls(i), k, x, ls(i));
}
maintain(i);
}
}
合并
M e r g e ( x , y ) Merge(x,y) Merge(x,y) : 将子树 x x x , y y y 合并,并返回根节点编号(其中 x x x 子树的值小于等于 y y y 子树)
- 若有一个子树为空,则返回 x ∣ y x\,|\,y x∣y ,及非空的子树
- 通过比较 x x x 与 y y y 的 f i x fix fix 值,确定位置
int Merge(int x, int y) {
if(!x || !y) return x | y;
if(fix[x] > fix[y]) { //在堆中,x 在 y 的上方,值小于 y ,所以在 y 的左上方
rs(x) = Merge(rs(x), y); // x 的右子树与 y 合并
maintain(x);
return x;
}
else { //在堆中,x 在 y 的下方,值小于 y ,所以在 y 的左下方
ls(y) = Merge(x, ls(y)); // x 和 y 的左子树合并
maintain(y);
return y;
}
}
插入操作
i n s e r t ( v ) insert(v) insert(v) : 插入一个权值为 v v v 的节点
- 按 v v v 将树分裂
- 让 x x x 与新节点合并,再与 y y y 合并
void insert(int v) {
Split(rt, v, x, y);
rt = Merge(Merge(x, New(v)), y);
}
删除操作
d e l ( v ) del(v) del(v) : 删除权值为 v v v 的节点
- 分裂两次,一次按 v v v ,一次按 v − 1 v - 1 v−1
- 将 y y y 的根删除(将左右子树合并即可)
- 将三颗子树合并
void del(int v) {
Split(rt, v, x, z);
Split(x, v - 1, x, y);
y = Merge(ls(y), rs(y));
rt = Merge(Merge(x, y), z);
}
查询排名
r a n k ( v ) rank(v) rank(v) : 查询值 v v v 的排名
- 按 v − 1 v - 1 v−1 分裂,子树 x x x 的大小 + 1 +1 +1 即为答案(不可按 v v v 划分,可能有重复)
int rank(int v) {
Split(rt, v - 1, x, y);
int ret = siz[x] + 1;
rt = Merge(x, y);
return ret;
}
按排名查值,前驱,后继
k t h ( x , v ) kth(x,v) kth(x,v) : 返回子树 x x x 中排名为 v v v 的下标
int kth(int x, int k) {
while(1) {
if(k <= siz[ls(x)])
x = ls(x);
else if(k == siz[ls(x)] + 1)
return x;
else k -= siz[ls(x)] + 1, x = rs(x);
}
}
K t h ( k ) Kth(k) Kth(k) : 排名为 k k k 的数
int Kth(int k) {
return val[kth(rt, k)];
}
p r e ( v ) pre(v) pre(v) : 值 v v v 的前驱
- 按照 v − 1 v - 1 v−1 分裂, x x x 中排名最大的节点即为 v v v 的前驱
int pre(int v) {
Split(rt, v - 1, x, y);
int ret = val[kth(x, siz[x])];
rt = Merge(x, y);
return ret;
}
n x t ( v ) nxt(v) nxt(v) : 值 v v v 的后继
- 按照 v v v 分裂, y y y 中排名最小的节点即为 v v v 的后继
int nxt(int v) {
Split(rt, v, x, y);
int ret = val[kth(y, 1)];
rt = Merge(x, y);
return ret;
}
#include <bits/stdc++.h>
#define maxn 1000005
using namespace std;
int read() {
int ret = 0, f = 1;
char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch)) {
ret = (ret << 1) + (ret << 3) + (ch ^ '0');
ch = getchar();
}
return ret * f;
}
class FHQ_Treap {
private:
#define ls(k) ch[k][0]
#define rs(k) ch[k][1]
int ch[maxn][2], val[maxn], fix[maxn], siz[maxn], tot;
int rt, x, y, z;
void maintain(int x) { siz[x] = siz[ls(x)] + siz[rs(x)] + 1; }
int New(int v) {
val[++tot] = v;
fix[tot] = rand();
siz[tot] = 1;
return tot;
}
void Split(int i, int k, int& x, int& y) {
if (!i) x = y = 0;
else {
if (val[i] <= k) {
x = i;
Split(rs(i), k, rs(i), y);
}
else {
y = i;
Split(ls(i), k, x, ls(i));
}
maintain(i);
}
}
int Merge(int x, int y) {
if (!x || !y) return x | y;
if (fix[x] < fix[y]) {
rs(x) = Merge(rs(x), y);
maintain(x);
return x;
}
else {
ls(y) = Merge(x, ls(y));
maintain(y);
return y;
}
}
public:
void insert(int v) {
Split(rt, v, x, y);
rt = Merge(Merge(x, New(v)), y);
}
void del(int v) {
Split(rt, v, x, z);
Split(x, v - 1, x, y);
y = Merge(ls(y), rs(y));
rt = Merge(Merge(x, y), z);
}
int kth(int x, int k) {
while (1) {
if (k <= siz[ls(x)])
x = ls(x);
else if (k == siz[ls(x)] + 1)
return x;
else k -= siz[ls(x)] + 1, x = rs(x);
}
}
int rank(int v) {
Split(rt, v - 1, x, y);
int ret = siz[x] + 1;
rt = Merge(x, y);
return ret;
}
int Kth(int k) {
return val[kth(rt, k)];
}
int pre(int v) {
Split(rt, v - 1, x, y);
int ret = val[kth(x, siz[x])];
rt = Merge(x, y);
return ret;
}
int nxt(int v) {
Split(rt, v, x, y);
int ret = val[kth(y, 1)];
rt = Merge(x, y);
return ret;
}
}T;
int n, opt, x;
int main() {
srand(time(NULL));
n = read();
for (int i = 1; i <= n; i++) {
opt = read(), x = read();
if (opt == 1) T.insert(x);
if (opt == 2) T.del(x);
if (opt == 3) cout << T.rank(x) << endl;
if (opt == 4) cout << T.Kth(x) << endl;
if (opt == 5) cout << T.pre(x) << endl;
if (opt == 6) cout << T.nxt(x) << endl;
}
return 0;
}
可持久化
让FHQ Treap可持久化,只需要在操作时复制节点,以便不影响之前的版本
复制节点
c o p y n o d e ( x ) copynode(x) copynode(x) : 复制节点 x x x
int copynode(int x) {
val[++tot] = val[x]; siz[tot] = siz[x]; fix[tot] = fix[x];
ls(tot) = ls(x); rs(tot) = rs(x);
return tot;
}
void split(int i, int k, int& x, int& y) {
if (!i)x = y = 0;
else {
if (val[i] <= k) {
x = copynode(i);
split(rs(x), k, rs(x), y);
maintain(x);
}
else {
y = copynode(i);
split(ls(y), k, x, ls(y));
maintain(y);
}
}
}
int Merge(int x, int y) {
if (!x || !y)return x | y;
if (fix[x] < fix[y]) {
int p = copynode(x);
rs(p) = Merge(rs(p), y);
maintain(p); return p;
}
else {
int p = copynode(y);
ls(p) = Merge(x, ls(p));
maintain(p); return p;
}
}
r t → r t [ i ] rt \rightarrow rt[i] rt→rt[i] 来记录不同版本的根节点
#include<bits/stdc++.h>
#define maxn 500005
using namespace std;
inline int read() {
int ret = 0, f = 1;
char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch)) {
ret = (ret << 1) + (ret << 3) + (ch ^ '0');
ch = getchar();
}
return ret * f;
}
int rt[maxn];
class FHQ_Treap {
private:
#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
int ch[maxn * 50][2];
int siz[maxn * 50], fix[maxn * 50], val[maxn * 50];
int tot;
inline int copynode(int x) {
siz[++tot] = siz[x]; fix[tot] = fix[x]; val[tot] = val[x];
ch[tot][0] = ch[x][0]; ch[tot][1] = ch[x][1];
return tot;
}
inline void maintain(int x) {
siz[x] = siz[ls(x)] + siz[rs(x)] + 1;
}
inline int New(int x) {
val[++tot] = x; siz[tot] = 1; fix[tot] = rand();
return tot;
}
inline int Merge(int x, int y) {
if (!x || !y)return x | y;
if (fix[x] < fix[y]) {
int p = copynode(x);
rs(p) = Merge(rs(p), y);
maintain(p); return p;
}
else {
int p = copynode(y);
ls(p) = Merge(x, ls(p));
maintain(p); return p;
}
}
inline void split(int i, int k, int& x, int& y) {
if (!i)x = y = 0;
else {
if (val[i] <= k) {
x = copynode(i);
split(rs(x), k, rs(x), y);
maintain(x);
}
else {
y = copynode(i);
split(ls(y), k, x, ls(y));
maintain(y);
}
}
}
public:
inline void Delete(int& root, int v) {
int x = 0, y = 0, z = 0;
split(root, v, x, z);
split(x, v - 1, x, y);
y = Merge(ls(y), rs(y));
root = Merge(Merge(x, y), z);
}
inline void Insert(int& root, int v) {
int x = 0, y = 0, z = 0;
split(root, v, x, y);
root = Merge(Merge(x, New(v)), y);
}
inline int kth(int k, int v) {
if (v == siz[ls(k)] + 1)return val[k];
else if (v <= siz[ls(k)])return kth(ls(k), v);
else return kth(rs(k), v - siz[ls(k)] - 1);
}
inline int rank(int& root, int v) {
int x, y;
split(root, v - 1, x, y);
int ans = siz[x] + 1;
root = Merge(x, y);
return ans;
}
inline int pre(int& root, int v) {
int x, y, k, ans;
split(root, v - 1, x, y);
if (!x)return -2147483647;
k = siz[x];
ans = kth(x, k);
root = Merge(x, y);
return ans;
}
inline int nex(int& root, int w) {
int x, y, ans;
split(root, w, x, y);
if (!y)return 2147483647;
else ans = kth(y, 1);
root = Merge(x, y);
return ans;
}
}T;
int n;
int main() {
n = read();
for (register int i = 1, opt, v, tim; i <= n; ++i) {
tim = read(), opt = read(), v = read();
rt[i] = rt[tim];
if (opt == 1) T.Insert(rt[i], v);
else if (opt == 2) T.Delete(rt[i], v);
else if (opt == 3) printf("%d\n", T.rank(rt[i], v));
else if (opt == 4) printf("%d\n", T.kth(rt[i], v));
else if (opt == 5) printf("%d\n", T.pre(rt[i], v));
else printf("%d\n", T.nex(rt[i], v));
}
return 0;
}