平衡树的学习

1、Treap

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

Splay介绍

3、LeafyTree (新算法,目前看来比较实用)

转载自其他网站的介绍\(PDF\)

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)操作的模拟:

666

对于FHQ-Treap合并(merge)操作的模拟:
666

(这些函数不是自己原创的,所以深深的为写出他们的人而赞叹,真的是太巧妙了!!!)

posted @ 2022-09-24 07:54  雪之下,树之旁  阅读(16)  评论(0编辑  收藏  举报