FHQ Treap(无旋Treap)、可持久化平衡树
BB
屯板!
Introduction
FHQ Treap,也称无旋Treap,顾名思义,就是不依靠rotate操作的Treap。FHQ Treap通过split和merge操作完成插入、删除等操作,由于不需要旋转,还可以用于实现 可持久化平衡树
关于FHQ Treap的资料参见下面各题在洛谷的题解
【模板】普通平衡树 - luogu P3369
Description
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1 插入x数
2 删除x数(若有多个相同的数,因只删除一个)
3 查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名)
4 查询排名为x的数
5 求x的前驱(前驱定义为小于x,且最大的数)
6 求x的后继(后继定义为大于x,且最小的数)
Sample Input
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
Sample Output
106465
84185
492737
Solution
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
using namespace std;
const int N = (int)1e5 + 15;
const int inf = 0x3f3f3f3f;
int ch[N][2], w[N], pri[N], sz[N];
int tot;
inline int mrand() {
static int seed = 123456;
return seed = (1LL * seed * 2333 + 1234567891) % 998244353;
}
inline int newNode(int v) {
int x = ++tot;
pri[x] = mrand();
sz[x] = 1;
w[x] = v;
return x;
}
inline void pushUp(int x) {
sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + 1;
}
inline int merge(int x, int y) {
if(!x || !y) {
return x | y;
}
if(pri[x] < pri[y]) {
ch[x][1] = merge(ch[x][1], y);
pushUp(x);
return x;
} else {
ch[y][0] = merge(x, ch[y][0]);
pushUp(y);
return y;
}
}
inline void split(int rt, int k, int& x, int& y) {
if(!rt) {
x = y = 0;
return;
}
if(w[rt] <= k) {
x = rt;
split(ch[rt][1], k, ch[x][1], y);
} else {
y = rt;
split(ch[rt][0], k, x, ch[y][0]);
}
pushUp(rt);
}
inline int kth(int rt, int k) {
if(sz[ch[rt][0]] + 1 == k) {
return rt;
} else if(sz[ch[rt][0]] >= k) {
return kth(ch[rt][0], k);
} else {
return kth(ch[rt][1], k - sz[ch[rt][0]] - 1);
}
}
int main() {
int rt = 0, m;
scanf("%d", &m);
while(m--) {
int op, x;
scanf("%d%d", &op, &x);
if(op == 1) {
int l, r;
split(rt, x, l, r);
rt = merge(l, merge(newNode(x), r));
} else if(op == 2) {
int p, q, r;
split(rt, x, q, r);
split(q, x - 1, p, q);
q = merge(ch[q][0], ch[q][1]);
rt = merge(p, merge(q, r));
} else if(op == 3) {
int l, r;
split(rt, x - 1, l, r);
printf("%d\n", sz[l] + 1);
rt = merge(l, r);
} else if(op == 4) {
printf("%d\n", w[kth(rt, x)]);
} else if(op == 5) {
int l, r;
split(rt, x - 1, l, r);
printf("%d\n", w[kth(l, sz[l])]);
rt = merge(l, r);
} else {
int l, r;
split(rt, x, l, r);
printf("%d\n", w[kth(r, 1)]);
rt = merge(l, r);
}
}
return 0;
}
【模板】文艺平衡树(Splay) - luogu P3391
Description
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1
Sample Input
5 3
1 3
1 3
1 4
Sample Output
4 3 2 1 5
Solution
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
using namespace std;
const int N = (int)1e5 + 15;
const int inf = 0x3f3f3f3f;
bool f[N];
int ch[N][2], w[N], pri[N], sz[N];
int tot;
inline int mrand() {
static int seed = 123456;
return seed = (1LL * seed * 2333 + 1234567891) % 998244353;
}
inline int newNode(int v) {
int x = ++tot;
pri[x] = mrand();
sz[x] = 1;
w[x] = v;
return x;
}
inline void pushDown(int x) {
if(f[x]) {
swap(ch[x][0], ch[x][1]);
f[ch[x][0]] ^= 1;
f[ch[x][1]] ^= 1;
f[x] = 0;
}
}
inline void pushUp(int x) {
sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + 1;
}
inline int merge(int x, int y) {
if(!x || !y) {
return x | y;
}
if(pri[x] < pri[y]) {
pushDown(x);
ch[x][1] = merge(ch[x][1], y);
pushUp(x);
return x;
} else {
pushDown(y);
ch[y][0] = merge(x, ch[y][0]);
pushUp(y);
return y;
}
}
inline void split(int rt, int k, int& x, int& y) {
if(!rt) {
x = y = 0;
return;
}
pushDown(rt);
if(sz[ch[rt][0]] < k) {
x = rt;
split(ch[rt][1], k - sz[ch[rt][0]] - 1, ch[x][1], y);
} else {
y = rt;
split(ch[rt][0], k, x, ch[y][0]);
}
pushUp(rt);
}
inline void show(int x) {
if(!x) {
return;
}
pushDown(x);
show(ch[x][0]);
printf("%d ", w[x]);
show(ch[x][1]);
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
int rt = 0;
for(int i = 1; i <= n; i++) {
int x = newNode(i);
rt = merge(rt, x);
}
while(m--) {
int l, r;
scanf("%d%d", &l, &r);
int x, y, z;
split(rt, l - 1, x, y);
split(y, r - l + 1, y, z);
f[y] ^= 1;
rt = merge(x, merge(y, z));
}
show(rt);
puts("");
return 0;
}
【模板】可持久化平衡树 - luogu P3835
Description
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作(对于各个以往的历史版本):
1 插入x数
2 删除x数(若有多个相同的数,因只删除一个,如果没有请忽略该操作)
3 查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名)
4 查询排名为x的数
5 求x的前驱(前驱定义为小于x,且最大的数,如不存在输出-2147483647)
6 求x的后继(后继定义为大于x,且最小的数,如不存在输出2147483647)
和原本平衡树不同的一点是,每一次的任何操作都是基于某一个历史版本,同时生成一个新的版本。(操作3, 4, 5, 6即保持原版本无变化)
每个版本的编号即为操作的序号(版本0即为初始状态,空树)
Sample Input
10
0 1 9
1 1 3
1 1 10
2 4 2
3 3 9
3 1 2
6 4 1
6 2 9
8 6 3
4 5 8
Sample Output
9
1
2
10
3
Solution
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
using namespace std;
const int N = (int)5e5 + 15;
const int inf = 0x3f3f3f3f;
int ch[N << 7][2], w[N << 7], pri[N << 7], sz[N << 7];
int root[N];
int tot;
inline int mrand() {
static int seed = 123456;
return seed = (1LL * seed * 2333 + 1234567891) % 998244353;
}
inline int newNode(int v) {
int x = ++tot;
pri[x] = mrand();
sz[x] = 1;
w[x] = v;
return x;
}
inline int copyNode(int v) {
if(!v) {
return 0;
}
int x = ++tot;
pri[x] = pri[v];
sz[x] = sz[v];
w[x] = w[v];
ch[x][0] = ch[v][0];
ch[x][1] = ch[v][1];
return x;
}
inline void pushUp(int x) {
sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + 1;
}
inline int merge(int x, int y) {
if(!x || !y) {
return x | y;
}
if(pri[x] < pri[y]) {
ch[x][1] = merge(ch[x][1], y);
pushUp(x);
return x;
} else {
ch[y][0] = merge(x, ch[y][0]);
pushUp(y);
return y;
}
}
inline void split(int rt, int k, int& x, int& y) {
if(!rt) {
x = y = 0;
return;
}
rt = copyNode(rt);
if(w[rt] <= k) {
x = rt;
split(ch[rt][1], k, ch[x][1], y);
} else {
y = rt;
split(ch[rt][0], k, x, ch[y][0]);
}
pushUp(rt);
}
inline int kth(int rt, int k) {
if(sz[ch[rt][0]] + 1 == k) {
return rt;
} else if(sz[ch[rt][0]] >= k) {
return kth(ch[rt][0], k);
} else {
return kth(ch[rt][1], k - sz[ch[rt][0]] - 1);
}
}
int main() {
root[0] = merge(newNode(-2147483647), newNode(2147483647));
int m;
scanf("%d", &m);
for(int i = 1; i <= m; i++) {
int op, v, x;
scanf("%d%d%d", &v, &op, &x);
if(op == 1) {
int l, r;
split(root[v], x, l, r);
root[i] = merge(l, merge(newNode(x), r));
} else if(op == 2) {
int p, q, r;
split(root[v], x, q, r);
split(q, x - 1, p, q);
q = merge(ch[q][0], ch[q][1]);
root[i] = merge(p, merge(q, r));
} else if(op == 3) {
int l, r;
split(root[v], x - 1, l, r);
printf("%d\n", sz[l]);
root[i] = merge(l, r);
} else if(op == 4) {
root[i] = copyNode(root[v]);
printf("%d\n", w[kth(root[i], x + 1)]);
} else if(op == 5) {
int l, r;
split(root[v], x - 1, l, r);
printf("%d\n", w[kth(l, sz[l])]);
root[i] = merge(l, r);
} else {
int l, r;
split(root[v], x, l, r);
printf("%d\n", w[kth(r, 1)]);
root[i] = merge(l, r);
}
}
return 0;
}
【模板】可持久化文艺平衡树 - luogu P5055
Description
您需要写一种数据结构,来维护一个序列,其中需要提供以下操作(对于各个以往的历史版本):
1 在第 ppp 个数后插入数 xxx 。
2 删除第 ppp 个数。
3 翻转区间 [l,r]
,例如原序列是 {5,4,3,2,1},翻转区间 [2,4]
后,结果是 {5,2,3,4,1}。
4 查询区间 [l,r]
中所有数的和。
和原本平衡树不同的一点是,每一次的任何操作都是基于某一个历史版本,同时生成一个新的版本(操作 444 即保持原版本无变化),新版本即编号为此次操作的序号。
本题强制在线。
Sample Input
10
0 1 0 1
1 1 1 2
2 4 1 2
3 1 2 0
4 4 2 1
5 3 5 7
6 4 5 6
4 1 7 1
8 3 4 6
9 4 4 1
Sample Output
3
4
5
10
Solution
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
using namespace std;
typedef long long ll;
const int N = (int)2e5 + 15;
const int inf = 0x3f3f3f3f;
int ch[N << 7][2], w[N << 7], pri[N << 7], sz[N << 7], f[N << 7];
ll sum[N << 7];
int root[N];
int tot;
inline int mrand() {
static int seed = 123456;
return seed = (1LL * seed * 2333 + 1234567891) % 998244353;
}
inline int newNode(int v) {
int x = ++tot;
pri[x] = mrand();
sz[x] = 1;
sum[x] = w[x] = v;
return x;
}
inline int copyNode(int v) {
if(!v) {
return 0;
}
int x = ++tot;
sum[x] = sum[v];
pri[x] = pri[v];
sz[x] = sz[v];
w[x] = w[v];
ch[x][0] = ch[v][0];
ch[x][1] = ch[v][1];
f[x] = f[v];
return x;
}
inline void pushUp(int x) {
sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + 1;
sum[x] = sum[ch[x][0]] + sum[ch[x][1]] + w[x];
}
inline void pushDown(int x) {
if(f[x]) {
ch[x][0] = copyNode(ch[x][0]);
ch[x][1] = copyNode(ch[x][1]);
swap(ch[x][0], ch[x][1]);
f[ch[x][0]] ^= 1;
f[ch[x][1]] ^= 1;
f[x] = 0;
}
}
inline int merge(int x, int y) {
if(!x || !y) {
return x | y;
}
if(pri[x] < pri[y]) {
pushDown(x);
ch[x][1] = merge(ch[x][1], y);
pushUp(x);
return x;
} else {
pushDown(y);
ch[y][0] = merge(x, ch[y][0]);
pushUp(y);
return y;
}
}
inline void split(int rt, int k, int& x, int& y) {
if(!rt) {
x = y = 0;
return;
}
pushDown(rt);
rt = copyNode(rt);
if(sz[ch[rt][0]] < k) {
x = rt;
split(ch[rt][1], k - sz[ch[rt][0]] - 1, ch[x][1], y);
} else {
y = rt;
split(ch[rt][0], k, x, ch[y][0]);
}
pushUp(rt);
}
int main() {
int m;
ll lstAns = 0;
scanf("%d", &m);
for(int i = 1; i <= m; i++) {
int op, v;
scanf("%d%d", &v, &op);
if(op == 1) {
ll p, x;
scanf("%lld%lld", &p, &x);
p ^= lstAns;
x ^= lstAns;
int l, r;
split(root[v], p, l, r);
root[i] = merge(l, merge(newNode(x), r));
} else if(op == 2) {
ll p;
scanf("%lld", &p);
p ^= lstAns;
int l, r, z;
split(root[v], p, l, r);
split(l, p - 1, l, z);
root[i] = merge(l, r);
} else if(op == 3) {
ll p, q;
scanf("%lld%lld", &p, &q);
p ^= lstAns;
q ^= lstAns;
int a, b, c;
split(root[v], p - 1, a, b);
split(b, q - p + 1, b, c);
f[b] ^= 1;
root[i] = merge(a, merge(b, c));
} else {
ll p, q;
scanf("%lld%lld", &p, &q);
p ^= lstAns;
q ^= lstAns;
int a, b, c;
split(root[v], p - 1, a, b);
split(b, q - p + 1, b, c);
lstAns = sum[b];
root[i] = merge(a, merge(b, c));
printf("%lld\n", lstAns);
}
}
return 0;
}