• [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
    ", S.size[S.ch[S.root][0]]);
    116     }else if (opt == 4) printf("%d
    ", S.key[S.Kth(S.root, x+1)]);
    117     else if (opt == 5) printf("%d
    ", S.key[S.get_las(x)]);
    118     else if (opt == 6) printf("%d
    ", 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
    ", S.rank(S.root, x)-1);
    110     else if (opt == 4) printf("%d
    ", S.get_num(S.root, x+1));
    111     else if (opt == 5) printf("%d
    ", S.get_num(S.root, S.rank(S.root, x)-1));
    112     else printf("%d
    ", 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
    ", T.rank(T.root, x));
    108     else if (opt == 4) printf("%d
    ", T.get_num(T.root, x));
    109     else if (opt == 5) printf("%d
    ", T.get_pre(T.root, x, -INF));
    110     else printf("%d
    ", 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
    ", T.rank(x));
    104     else if (opt == 4) printf("%d
    ", T.get_num(T.root, x));
    105     else if (opt == 5) printf("%d
    ", T.get_pre(x));
    106     else printf("%d
    ", T.get_nex(x));
    107     }
    108 }
    109 int main() {
    110     work();
    111     return 0;
    112 }
    fhq_Treap
  • 相关阅读:
    chrome浏览器中安装以及使用Elasticsearch head 插件
    windows10 升级并安装配置 jmeter5.3
    linux下部署Elasticsearch6.8.1版本的集群
    【Rollo的Python之路】Python 爬虫系统学习 (八) logging模块的使用
    【Rollo的Python之路】Python 爬虫系统学习 (七) Scrapy初识
    【Rollo的Python之路】Python 爬虫系统学习 (六) Selenium 模拟登录
    【Rollo的Python之路】Python 爬虫系统学习 (五) Selenium
    【Rollo的Python之路】Python 爬虫系统学习 (四) XPath学习
    【Rollo的Python之路】Python 爬虫系统学习 (三)
    【Rollo的Python之路】Python sys argv[] 函数用法笔记
  • 原文地址:https://www.cnblogs.com/NaVi-Awson/p/7919884.html
Copyright © 2020-2023  润新知