平衡树的学习
1、Treap
这是我自己写的Treap, 通过了洛谷加强数据的测试,应该无锅,先上代码:
点击这里查看我的代码哦!
/*2022.9.28 完工(中午写了一遍,到晚上才 AC)*/
#pragma comment(linker, "/STACK:102400000,102400000")//手动开大栈区
#include <bits/stdc++.h>
using namespace std;
#define N 2000010
#define int long long
#define ll long long
const ll INF = 2e15;
template <class T>
inline void read(T& a){
T x = 0, s = 1;
char c = getchar();
while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
a = x * s;
return ;
}
struct node{
int val, pri, siz, cnt, ch[2];
} t[N];
int root = 0;
int tot = 0;
#define lson ch[0]
#define rson ch[1]
int build(int x){
t[++tot].val = x;
t[tot].pri = (ll)rand() * rand() % (ll)1e15 + 1;
t[tot].siz = 1;
t[tot].cnt = 1;
return tot;
}
inline void pushup(int now){
t[now].siz = t[t[now].lson].siz + t[t[now].rson].siz + t[now].cnt;
return ;
}
void rotate(int& now, int d){
int x = t[now].ch[d^1];
t[now].ch[d^1] = t[x].ch[d];
t[x].ch[d] = now;
now = x;
pushup(t[now].ch[d]); pushup(now);
return ;
}
void insert(int& now, int x){
if(!now){
now = build(x);
return ;
}
if(t[now].val == x){
t[now].cnt++;
}
else{
int d = x < t[now].val ? 0 : 1;
insert(t[now].ch[d], x);
if(t[now].pri < t[t[now].ch[d]].pri) rotate(now, d ^ 1);
}
pushup(now);
return ;
}
void del(int& now, int x){
if(!now) return ;
if(t[now].val == x) {
if(t[now].cnt >= 2) t[now].cnt--, pushup(now);
else{
if(t[now].lson || t[now].rson){
if(!t[now].rson || t[t[now].lson].pri > t[t[now].rson].pri){
rotate(now, 1);
del(t[now].rson, x);
}
else rotate(now, 0), del(t[now].lson, x);
}
else now = 0; // 直接删除
}
}
else{
if(x < t[now].val) del(t[now].lson, x);
else del(t[now].rson, x);
pushup(now);
}
pushup(now);
return ;
}
int get_rank(int& now, int x){
if(!now) return 1; // 对于找不到的情况,前一个的数 + 1(虽然在一开始已经插入了,不可能找不到)
if(t[now].val == x){
return t[t[now].lson].siz + 1;
}
if(t[now].val < x){
return t[t[now].lson].siz + t[now].cnt + get_rank(t[now].rson, x);
}
if(t[now].val > x){
return get_rank(t[now].lson, x);
}
return 0;
}
int find_kth(int& now, int k){
if(!now) return 0;
if(t[t[now].lson].siz >= k) return find_kth(t[now].lson, k);
else if(t[t[now].lson].siz + t[now].cnt >= k) return t[now].val;
else return find_kth(t[now].rson, k - t[t[now].lson].siz - t[now].cnt);
}
int get_pre(int now, int x){
if(!now) return -INF;
if(t[now].val >= x) return get_pre(t[now].lson, x);
else return max(t[now].val, get_pre(t[now].rson, x));
}
int get_next(int now, int x){
if(!now) return INF;
if(t[now].val <= x) return get_next(t[now].rson, x);
else return min(t[now].val, get_next(t[now].lson, x));
}
signed main(){
// freopen("hh.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
srand(time(0));
int n, Q;
read(n), read(Q);
for(int i = 1; i <= n; i++){
int x; read(x);
insert(root, x);
}
int ans = 0, last = 0;
while(Q--){
int opt, x;
read(opt), read(x);
x ^= last;
if(opt == 1) insert(root, x);
else if(opt == 2) del(root, x);
else if(opt == 3){
insert(root, x);
last = get_rank(root, x);
del(root, x);
ans ^= last;
}
else if(opt == 4){
last = find_kth(root, x);
ans ^= last;
}
else if(opt == 5){
last = get_pre(root, x);
ans ^= last;
}
else if(opt == 6){
last = get_next(root, x);
ans ^= last;
}
}
cout << ans << endl;
return 0;
}
易错点总结(查了好久的)
1、一定要注意pushup, 这个不能忘,不然分分钟 \(WA\) 。
2、旋转时,左旋还是右旋要分清楚,脑海中要明确二者的样子。
3、在计算 \(size\) 子树大小的时候,要注意哪里是 \(+1\), 哪里是 \(+cnt\)。
2、Splay
3、LeafyTree (新算法,目前看来比较实用)
4、FHQ treap
FHQ-Treap 全功能实现代码
#pragma comment(linker, "/STACK:102400000,102400000")//手动开大栈区
/* 完整版 FHQ treap 2022.10.4 /*
/*FHQ treap 应注意事项:
当前所用写法,会有多个节点有相同的值,故不需要统计 cnt。
*/
#include <bits/stdc++.h>
using namespace std;
#define N 2000010
#define int long long
#define ll long long
const ll INF = 2e15;
template <class T>
inline void read(T& a){
T x = 0, s = 1;
char c = getchar();
while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
a = x * s;
return ;
}
struct node{
int val, pri, siz, ch[2];
int rev;
} t[N];
int root = 0;
int tot = 0;
#define lson ch[0]
#define rson ch[1]
int stac[N], top = 0;
int build(int x){
int now = top ? stac[top--] : ++tot;
t[now].val = x;
t[now].pri = (ll)rand() * rand() % (ll)1e15 + 1;
t[now].siz = 1;
t[now].lson = t[now].rson = 0;
return now;
}
inline void pushup(int now){
t[now].siz = t[t[now].lson].siz + t[t[now].rson].siz + 1;
return ;
}
inline void pushdown(int now){ // 下传旋转标记
if(!t[now].rev) return ;
t[t[now].lson].rev ^= 1;
t[t[now].rson].rev ^= 1;
swap(t[now].lson, t[now].rson); // 真的进行翻转
t[now].rev = 0;
return ;
}
void split(int now, int key, int& x, int& y){ // 按值分裂
if(!now){
x = y = 0;
return ;
}
if(t[now].val <= key){ // "<=" 表明key也会在这棵树里面
x = now;
split(t[now].rson, key, t[now].rson, y); // 同时修改儿子,用 rson 连起来
}
else {
y = now;
split(t[now].lson, key, x, t[now].lson);
}
pushup(now);
return ;
}
void split_by_size(int now, int key, int& x, int& y){ // 按大小分裂
if(!now){ // 没有的分
x = y = 0;
return ;
}
pushdown(now);
if(t[t[now].lson].siz + 1 <= key){
x = now;
split_by_size(t[now].rson, key - t[t[now].lson].siz - 1, t[now].rson, y); // 大小注意减掉
}
else {
y = now;
split_by_size(t[now].lson, key, x, t[now].lson);
}
pushup(now);
return ;
}
// int merge(int x, int y){ // 合并是有要求的: x 所填的必须是比 y 小的才行
// if(!x || !y) return x + y;
// if(t[x].pri > t[y].pri){
// t[x].rson = merge(t[x].rson, y);
// pushup(x);
// return x;
// }
// else {
// t[y].lson = merge(x, t[y].lson);
// pushup(y);
// return y;
// }
// return 0;
// }
int merge(int x, int y){
if(!x || !y) return x + y;
if(t[x].pri > t[y].pri){
pushdown(x);
t[x].rson = merge(t[x].rson, y);
pushup(x);
return x;
}
else{
pushdown(y);
t[y].lson = merge(x, t[y].lson);
pushup(y);
return y;
}
return 0;
}
void insert(int key){
int x, y;
split(root, key - 1, x, y);
root = merge(merge(x, build(key)), y); // 不需要 cnt 统计,直接看 siz 即可
return ;
}
void del(int key){
int x, y, z;
split(root, key, x, z);
split(x, key - 1, x, y); // 在 x 树上进行分裂
if(y){
if(top < N) stac[++top] = y;
y = merge(t[y].lson, t[y].rson);
}
root = merge(merge(x, y), z); // 反向搞一波
return ;
}
int get_rank(int key){ // 获取某个数的排名
int ans, x, y, z;
split(root, key - 1, x, y);
ans = t[x].siz + 1;
root = merge(x, y);
return ans;
}
int get_kth(int now, int k){ // 获取第 k 个数
if(!now) return INF;
if(t[t[now].lson].siz + 1 == k) return t[now].val;
else if(t[t[now].lson].siz >= k) return get_kth(t[now].lson, k);
else return get_kth(t[now].rson, k - t[t[now].lson].siz - 1);
}
// int get_pre(int key){ // FHQ-Treap 特有方法
// int x, y;
// split(root, key - 1, x, y);
// int now = x;
// while(t[now].rson) now = t[now].rson;
// int ans = t[now].val;
// root = merge(x, y);
// return ans;
// }
// int get_next(int key){
// int x, y, ans, now;
// split(root, key, x, y);
// now = y;
// while(t[now].lson) now = t[now].lson;
// ans = t[now].val;
// root = merge(x, y);
// return ans;
// }
int get_pre(int now, int key){ // 通法
if(!now) return -INF;
if(key <= t[now].val) return get_pre(t[now].lson, key);
else return max(t[now].val, get_pre(t[now].rson, key));
}
int get_next(int now, int key){
if(!now) return INF;
if(key >= t[now].val) return get_next(t[now].rson, key);
else return min(t[now].val, get_next(t[now].lson, key));
}
/*区间旋转*/
void reverse(int l, int r){
int x, y, z;
split_by_size(root, l - 1, x, y);
split_by_size(y, r - l + 1, y, z);
t[y].rev ^= 1;
root = merge(x, merge(y, z));
return ;
}
void dfs(int now){
if(!now) return ;
pushdown(now);
dfs(t[now].lson);
printf("%d ", t[now].val); // 中序遍历
dfs(t[now].rson);
pushup(now);
return ;
}
/*区间旋转结束*/
#undef lson
#undef rson
signed main(){
// freopen("hh.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
srand(time(0));
int n, Q;
read(n), read(Q);
for(int i = 1; i <= n; i++){
int x; read(x);
insert(x);
}
int ans = 0, last = 0;
while(Q--){
int opt, x;
read(opt), read(x);
x ^= last;
if(opt == 1) insert(x);
else if(opt == 2) del(x);
else if(opt == 3){
last = get_rank(x);
ans ^= last;
}
else if(opt == 4){
last = get_kth(root, x);
ans ^= last;
}
else if(opt == 5){
last = get_pre(root, x);
ans ^= last;
}
else if(opt == 6){
last = get_next(root, x);
ans ^= last;
}
/*else if(opt == 7){
read(l),read(r);
reverse(l, r);
}*/
}
cout << ans << endl;
return 0;
}
对于FHQ-Treap分裂(split)操作的模拟:
对于FHQ-Treap合并(merge)操作的模拟: