[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
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
Sample Output
84185
492737
HINT
题解
作为一个模板题放在这里,准备写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 }
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 }
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 }
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 }