[BZOJ 3224]Tyvj 1728 普通平衡树

Description

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)

Input

第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)

Output

对于操作3,4,5,6每行输出一个数,表示对应答案

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

HINT

1.n的数据范围:n<=100000
2.每个数的数据范围:[-2e9,2e9]

题解

作为一个模板题放在这里,准备写4个常用的平衡树。算是个坑...

1 Splay

1.1 插入操作

一般平衡树的操作,不多赘述。

1.2 删除操作

我们考虑删除数$x$:我们将$x$的前驱和后继找到。两个找到之后再将前驱通过$splay$操作旋到根,再将后继旋成前驱的儿子,删除后继的左儿子即可。
显然能够保证后继的左儿子只有一个。
至于为什么要前驱和后继都找到之后再旋转,是因为查找的时候会将其他的节点旋转到根。

1.3 查询名次

我们通过维护子树大小来实现这一功能。
某个数的名次其实就是当它旋转到根的时候左子树的大小+1。
我们对于每次插入操作,删除操作,$rotate$操作都要更新名次。

1.4 查询Kth数

递归实现。
若左子树大小+1 <= 查询名次 <= 左子树大小+根节点数个数,结果就是当前数;
若查询名次 <= 左子树大小+1,递归查询左子树;
若查询名次 >= 左子树大小+根节点数个数,将查询名次-=左子树大小+根节点数个数,递归查询右子树。

1.5 查找前驱

通过$find$操作,找到“接近”这个数的数的位置。
为什么是“接近”,是因为可能$splay$中可能不存在这个数,不存在的话我们就假想最后停在的节点就是这个数。
将其旋到根。判断这个数与根节点的数的关系,若小于(即不存在查询的数)显然这个数就是前驱。
若非,就查询其左子树“最靠右的那个数”。

1.6 查找后继

和查找前驱类似。

  1 //It is made by Awson on 2017.11.28
  2 #include <map>
  3 #include <set>
  4 #include <cmath>
  5 #include <ctime>
  6 #include <queue>
  7 #include <stack>
  8 #include <cstdio>
  9 #include <string>
 10 #include <vector>
 11 #include <cstdlib>
 12 #include <cstring>
 13 #include <iostream>
 14 #include <algorithm>
 15 #define LL long long
 16 #define Max(a, b) ((a) > (b) ? (a) : (b))
 17 #define Min(a, b) ((a) < (b) ? (a) : (b))
 18 using namespace std;
 19 const int N = 100000;
 20 const int INF = ~0u>>1;
 21 
 22 struct Splay_tree {
 23     int pre[N+5], ch[N+5][2], key[N+5], size[N+5], cnt[N+5], tot, root;
 24     queue<int>mem;
 25     void newnode(int &o, int keyy, int fa) {
 26     if (!mem.empty()) o = mem.front(), mem.pop();
 27     else o = ++tot;
 28     pre[o] = fa, ch[o][0] = ch[o][1] = 0;
 29     key[o] = keyy, cnt[o] = size[o] = 1;
 30     }
 31     void pushup(int o) {
 32     size[o] = size[ch[o][0]]+size[ch[o][1]]+cnt[o];
 33     }
 34     void rotate(int o, int kind) {
 35     int p = pre[o];
 36     ch[p][!kind] = ch[o][kind], pre[ch[o][kind]] = p;
 37     ch[pre[p]][ch[pre[p]][1] == p] = o, pre[o] = pre[p];
 38     ch[o][kind] = p, pre[p] = o;
 39     pushup(p), pushup(o);
 40     }
 41     void splay(int o, int goal) {
 42     while (pre[o] != goal) {
 43         if (pre[pre[o]] == goal) rotate(o, ch[pre[o]][0] == o);
 44         else {
 45         int p = pre[o], kind = ch[pre[p]][0] == p;
 46         if (ch[p][kind] == o) rotate(o, !kind), rotate(o, kind);
 47         else rotate(p, kind), rotate(o, kind);
 48         }
 49     }
 50     if (goal == 0) root = o;
 51     }
 52     void insert(int &o, int keyy, int fa) {
 53     if (o == 0) {
 54         newnode(o, keyy, fa); splay(o, 0); return;
 55     }
 56     size[o]++;
 57     if (key[o] == keyy) {
 58         cnt[o]++; splay(o, 0); return;
 59     }
 60     insert(ch[o][keyy > key[o]], keyy, o);
 61     }
 62     void find(int o, int keyy) {
 63     if (o == 0) return;
 64     if (key[o] == keyy) {
 65         splay(o, 0); return;
 66     }
 67     if (ch[o][keyy > key[o]]) find(ch[o][keyy > key[o]], keyy);
 68     else splay(o, 0);
 69     }
 70     int get_las(int keyy) {
 71     find(root, keyy);
 72     if (keyy > key[root]) return root;
 73     int u = ch[root][0];
 74     while (ch[u][1]) u = ch[u][1];
 75     return u;
 76     }
 77     int get_nex(int keyy) {
 78     find(root, keyy);
 79     if (keyy < key[root]) return root;
 80     int u = ch[root][1];
 81     while (ch[u][0]) u = ch[u][0];
 82     return u;
 83     }
 84     void delet(int keyy) {
 85     int las = get_las(keyy);
 86     int nex = get_nex(keyy);
 87     splay(las, 0), splay(nex, las);
 88     size[las]--, size[nex]--;
 89     if (cnt[ch[nex][0]] > 1) {
 90         cnt[ch[nex][0]]--; size[ch[nex][0]]--;
 91         splay(ch[nex][0], 0);
 92     }else {
 93         mem.push(ch[nex][0]);
 94         ch[nex][0] = 0;
 95     }
 96     }
 97     int Kth(int o, int rest) {
 98     if (rest >= size[ch[o][0]]+1 && rest <= size[ch[o][0]]+cnt[o]) return o;
 99     if (rest > size[ch[o][0]]+cnt[o]) return Kth(ch[o][1], rest-size[ch[o][0]]-cnt[o]);
100     return Kth(ch[o][0], rest);
101     }
102 }S;
103 int n, opt, x;
104 
105 void work() {
106     S.insert(S.root, INF, 0);
107     S.insert(S.root, -INF, 0);
108     scanf("%d", &n);
109     while (n--) {
110     scanf("%d%d", &opt, &x);
111     if (opt == 1) S.insert(S.root, x, 0);
112     else if (opt == 2) S.delet(x);
113     else if (opt == 3) {
114         S.find(S.root, x);
115         printf("%d\n", S.size[S.ch[S.root][0]]);
116     }else if (opt == 4) printf("%d\n", S.key[S.Kth(S.root, x+1)]);
117     else if (opt == 5) printf("%d\n", S.key[S.get_las(x)]);
118     else if (opt == 6) printf("%d\n", S.key[S.get_nex(x)]);
119     }
120 }
121 int main() {
122     work();
123     return 0;
124 }
Splay

2 替罪羊树

替罪羊树的平衡是基于子树大小平衡的。

对于树中的结点$o$若存在$size[o]*\alpha >= size[ch[o][0]]$ 且 $size[o]*\alpha >= size[ch[o][1]]$,则对于以$o$为根的子树是平衡的。若不平衡直接将子树暴力拍平重建。

  1 //It is made by Awson on 2017.12.8
  2 #include <map>
  3 #include <set>
  4 #include <cmath>
  5 #include <ctime>
  6 #include <queue>
  7 #include <stack>
  8 #include <cstdio>
  9 #include <string>
 10 #include <vector>
 11 #include <cstdlib>
 12 #include <cstring>
 13 #include <iostream>
 14 #include <algorithm>
 15 #define LL long long
 16 #define Max(a, b) ((a) > (b) ? (a) : (b))
 17 #define Min(a, b) ((a) < (b) ? (a) : (b))
 18 using namespace std;
 19 const int N = 100000;
 20 const int INF = ~0u>>1;
 21 const double alpha = 0.75;
 22 
 23 struct Scapegoat_tree {
 24     int size[N+5], ch[N+5][2], key[N+5], del[N+5], pre[N+5], root, tot, tmp[N+5];
 25     queue<int>mem;
 26     void newnode(int &o, int keyy, int fa) {
 27     if (!mem.empty()) o = mem.front(), mem.pop();
 28     else o = ++tot;
 29     size[o] = 1, ch[o][0] = ch[o][1] = del[o] = 0;
 30     key[o] = keyy, pre[o] = fa;
 31     }
 32     bool balance(int o) {
 33     return size[o]*alpha >= size[ch[o][0]] && size[o]*alpha >= size[ch[o][1]];
 34     }
 35     void pushup(int o) {
 36     size[o] = size[ch[o][0]]+size[ch[o][1]]+(!del[o]);
 37     }
 38     void travel(int o, int &pos) {
 39     if (ch[o][0]) travel(ch[o][0], pos);
 40     if (!del[o]) tmp[++pos] = o;
 41     else mem.push(o);
 42     if (ch[o][1]) travel(ch[o][1], pos);
 43     }
 44     int device(int fa, int l, int r) {
 45     if (l > r) return 0;
 46     int mid = (l+r)>>1, o = tmp[mid];
 47     ch[o][0] = device(o, l, mid-1);
 48     ch[o][1] = device(o, mid+1, r);
 49     pre[o] = fa; size[o] = 1;
 50     pushup(o);
 51     return o;
 52     }
 53     void rebuild(int o) {
 54     int pos = 0; travel(o, pos);
 55     if (root == o) root = device(0, 1, pos);
 56     else {
 57         int t = ch[pre[o]][1] == o;
 58         ch[pre[o]][t] = device(pre[o], 1, pos);
 59     }
 60     }
 61     int Insert(int &o, int keyy, int fa) {
 62     if (!o) {
 63         newnode(o, keyy, fa); return 0;
 64     }
 65     size[o]++;
 66     int p = Insert(ch[o][keyy >= key[o]], keyy, o);
 67     if (!balance(o)) p = o;
 68     return p;
 69     }
 70     void insert(int &o, int keyy, int fa) {
 71     int x = Insert(o, keyy, fa); if (x) rebuild(x);
 72     }
 73     int rank(int o, int keyy) {
 74     if (!o) return 1;
 75     if (key[o] < keyy) return size[ch[o][0]]+(!del[o])+rank(ch[o][1], keyy);
 76     else return rank(ch[o][0], keyy);
 77     }
 78     int Erase(int o, int rank) {
 79     size[o]--;
 80     if (!del[o] && size[ch[o][0]]+(!del[o]) == rank) {
 81         del[o] = 1; return 0;
 82     }
 83     int p;
 84     if (size[ch[o][0]] >= rank) p = Erase(ch[o][0], rank);
 85     else p = Erase(ch[o][1], rank-(size[ch[o][0]]+(!del[o])));
 86     if (!balance(o)) p = o;
 87     return p;
 88     }
 89     void erase(int o, int keyy) {
 90     int rk = rank(o, keyy);
 91     int x = Erase(o, rk); if (x) rebuild(x);
 92     }
 93     int get_num(int o, int rank) {
 94     if (!del[o] && size[ch[o][0]]+(!del[o]) == rank) return key[o];
 95     if (size[ch[o][0]] >= rank) return get_num(ch[o][0], rank);
 96     return get_num(ch[o][1], rank-(size[ch[o][0]]+(!del[o])));
 97     }
 98 }S;
 99 int n, opt, x;
100 
101 void work() {
102     S.insert(S.root, INF, 0);
103     S.insert(S.root, -INF, 0);
104     scanf("%d", &n);
105     while (n--) {
106     scanf("%d%d", &opt, &x);
107     if (opt == 1) S.insert(S.root, x, 0);
108     else if (opt == 2) S.erase(S.root, x);
109     else if (opt == 3) printf("%d\n", S.rank(S.root, x)-1);
110     else if (opt == 4) printf("%d\n", S.get_num(S.root, x+1));
111     else if (opt == 5) printf("%d\n", S.get_num(S.root, S.rank(S.root, x)-1));
112     else printf("%d\n", S.get_num(S.root, S.rank(S.root, x+1)));
113     }
114 }
115 int main() {
116     work();
117     return 0;
118 }
Scapegoat

3 Treap

$Treap$的平衡是基于堆性质的。它的$key$值满足$BST$的性质,平衡参数$lev$满足堆性质。

  1 //It is made by Awson on 2017.12.11
  2 #include <map>
  3 #include <set>
  4 #include <cmath>
  5 #include <ctime>
  6 #include <queue>
  7 #include <stack>
  8 #include <cstdio>
  9 #include <string>
 10 #include <vector>
 11 #include <cstdlib>
 12 #include <cstring>
 13 #include <iostream>
 14 #include <algorithm>
 15 #define LL long long
 16 #define Max(a, b) ((a) > (b) ? (a) : (b))
 17 #define Min(a, b) ((a) < (b) ? (a) : (b))
 18 using namespace std;
 19 const int N = 100000;
 20 const int INF = ~0u>>1;
 21 
 22 struct Treap {
 23     int ch[N+5][2], lev[N+5], size[N+5], cnt[N+5], key[N+5], tot, root;
 24     queue<int>mem;
 25     void newnode(int &o, int keyy) {
 26     if (!mem.empty()) o = mem.front(), mem.pop();
 27     else o = ++tot;
 28     ch[o][0] = ch[o][1] = 0;
 29     cnt[o] = size[o] = 1;
 30     key[o] = keyy, lev[o] = rand();
 31     }
 32     void pushup(int o) {
 33     size[o] = size[ch[o][0]]+size[ch[o][1]]+cnt[o];
 34     }
 35     void rotate(int &o, int kind) {
 36     int x = ch[o][!kind];
 37     ch[o][!kind] = ch[x][kind];
 38     ch[x][kind] = o;
 39     o = x;
 40     }
 41     void insert(int &o, int keyy) {
 42     if (!o) {
 43         newnode(o, keyy); return;
 44     }
 45     size[o]++;
 46     if (key[o] == keyy) {
 47         cnt[o]++; return;
 48     }
 49     int kind = keyy >= key[o];
 50     insert(ch[o][kind], keyy);
 51     if (lev[ch[o][kind]] > lev[o]) {
 52         rotate(o, !kind); pushup(ch[o][!kind]), pushup(o);
 53     }
 54     }
 55     void delet(int &o, int keyy) {
 56     if (key[o] == keyy) {
 57         if (cnt[o] > 1) {
 58         size[o]--; cnt[o]--; return;
 59         }else {
 60         if (ch[o][0] && ch[o][1]) {
 61             int kind = lev[ch[o][0]] > lev[ch[o][1]];
 62             rotate(o, kind); pushup(ch[o][kind]), pushup(o);
 63             size[o]--;
 64             delet(ch[o][kind], keyy);
 65         }else {
 66             int kind = (bool)ch[o][1];
 67             mem.push(o);
 68             o = ch[o][kind];
 69         }
 70         }
 71     }else {
 72         size[o]--; delet(ch[o][keyy >= key[o]], keyy);
 73     }
 74     }
 75     int rank(int o, int keyy) {
 76     if (key[o] == keyy) return size[ch[o][0]]+1;
 77     if (key[o] < keyy) return size[ch[o][0]]+cnt[o]+rank(ch[o][1], keyy);
 78     return rank(ch[o][0], keyy);
 79     }
 80     int get_num(int o, int rk) {
 81     if (size[ch[o][0]]+1 <= rk && rk <= size[ch[o][0]]+cnt[o]) return key[o];
 82     if (rk <= size[ch[o][0]]) return get_num(ch[o][0], rk);
 83     return get_num(ch[o][1], rk-size[ch[o][0]]-cnt[o]);
 84     }
 85     int get_pre(int o, int keyy, int ans) {
 86     if (!o) return ans;
 87     if (key[o] >= keyy) return get_pre(ch[o][0], keyy, ans);
 88     ans = Max(ans, key[o]);
 89     return get_pre(ch[o][1], keyy, ans);
 90     }
 91     int get_nex(int o, int keyy, int ans) {
 92     if (!o) return ans;
 93     if (key[o] <= keyy) return get_nex(ch[o][1], keyy, ans);
 94     ans = Min(ans, key[o]);
 95     return get_nex(ch[o][0], keyy, ans);
 96     }
 97 }T;
 98 int n, opt, x;
 99 
100 void work() {
101     srand(time(0));
102     scanf("%d", &n);
103     while (n--) {
104     scanf("%d%d", &opt, &x);
105     if (opt == 1) T.insert(T.root, x);
106     else if (opt == 2) T.delet(T.root, x);
107     else if (opt == 3) printf("%d\n", T.rank(T.root, x));
108     else if (opt == 4) printf("%d\n", T.get_num(T.root, x));
109     else if (opt == 5) printf("%d\n", T.get_pre(T.root, x, -INF));
110     else printf("%d\n", T.get_nex(T.root, x, INF));
111     }
112 }
113 int main() {
114     work();
115     return 0;
116 }
Treap

4 fhq_Treap

一般的$Treap$的自平衡是依赖于$rotate$操作实现的,而无旋$Treap$则是依靠$split$和$merge$两个操作实现自平衡。

其主要的思想就是将一棵$Treap$分成多个。再将多个$Treap$合并。

坑终于填完了...

  1 //It is made by Awson on 2017.12.15
  2 #include <map>
  3 #include <set>
  4 #include <cmath>
  5 #include <ctime>
  6 #include <queue>
  7 #include <stack>
  8 #include <cstdio>
  9 #include <string>
 10 #include <vector>
 11 #include <cstdlib>
 12 #include <cstring>
 13 #include <iostream>
 14 #include <algorithm>
 15 #define LL long long
 16 #define Max(a, b) ((a) > (b) ? (a) : (b))
 17 #define Min(a, b) ((a) < (b) ? (a) : (b))
 18 using namespace std;
 19 const int N = 100000;
 20 const int INF = ~0u>>1;
 21 
 22 struct fhq_Treap {
 23     int ch[N+5][2], lev[N+5], size[N+5], key[N+5], tot, root;
 24     queue<int>mem;
 25     int newnode(int keyy) {
 26     int o;
 27     if (!mem.empty()) o = mem.front(), mem.pop();
 28     else o = ++tot;
 29     ch[o][0] = ch[o][1] = 0;
 30     size[o] = 1;
 31     key[o] = keyy, lev[o] = rand();
 32     return o;
 33     }
 34     void pushup(int o) {
 35     size[o] = size[ch[o][0]]+size[ch[o][1]]+1;
 36     }
 37     void split(int o, int keyy, int &x, int &y) {
 38     if (!o) x = y = 0;
 39     else {
 40         if (key[o] <= keyy) x = o, split(ch[o][1], keyy, ch[o][1], y);
 41         else y = o, split(ch[o][0], keyy, x, ch[o][0]);
 42         pushup(o);
 43     }
 44     }
 45     int merge(int x, int y) {
 46     if (!x || !y) return x+y;
 47     if (lev[x] < lev[y]) {
 48         ch[x][1] = merge(ch[x][1], y);
 49         pushup(x); return x;
 50     }else {
 51         ch[y][0] = merge(x, ch[y][0]);
 52         pushup(y); return y;
 53     }
 54     }
 55     void insert(int keyy) {
 56     int r1, r2; split(root, keyy, r1, r2);
 57     root = merge(merge(r1, newnode(keyy)), r2);
 58     }
 59     void delet(int keyy) {
 60     int r1, r2, r3; split(root, keyy-1, r1, r2);
 61     split(r2, keyy, r2, r3);
 62     mem.push(r2);
 63     r2 = merge(ch[r2][0], ch[r2][1]);
 64     root = merge(merge(r1, r2), r3);
 65     }
 66     int rank(int keyy) {
 67     int r1, r2; split(root, keyy-1, r1, r2);
 68     int ans = size[r1]+1;
 69     root = merge(r1, r2);
 70     return ans;
 71     }
 72     int get_num(int o, int rk) {
 73     if (size[ch[o][0]]+1 == rk) return key[o];
 74     if (size[ch[o][0]] >= rk) return get_num(ch[o][0], rk);
 75     return get_num(ch[o][1], rk-1-size[ch[o][0]]);
 76     }
 77     int get_pre(int keyy) {
 78     int r1, r2; split(root, keyy-1, r1, r2);
 79     int o = r1;
 80     while (ch[o][1]) o = ch[o][1];
 81     int ans = key[o];
 82     root = merge(r1, r2);
 83     return ans;
 84     }
 85     int get_nex(int keyy) {
 86     int r1, r2; split(root, keyy, r1, r2);
 87     int o = r2;
 88     while (ch[o][0]) o = ch[o][0];
 89     int ans = key[o];
 90     root = merge(r1, r2);
 91     return ans;
 92     }
 93 }T;
 94 int n, opt, x;
 95 
 96 void work() {
 97     srand(time(0));
 98     scanf("%d", &n);
 99     while (n--) {
100     scanf("%d%d", &opt, &x);
101     if (opt == 1) T.insert(x);
102     else if (opt == 2) T.delet(x);
103     else if (opt == 3) printf("%d\n", T.rank(x));
104     else if (opt == 4) printf("%d\n", T.get_num(T.root, x));
105     else if (opt == 5) printf("%d\n", T.get_pre(x));
106     else printf("%d\n", T.get_nex(x));
107     }
108 }
109 int main() {
110     work();
111     return 0;
112 }
fhq_Treap
posted @ 2017-12-16 14:01  NaVi_Awson  阅读(280)  评论(0编辑  收藏  举报