最近学了一下平衡树,想做一些笔记。
1.$Splay$
$Splay$是我学会的第一棵平衡树(滑稽)
$Splay$通过$Splay$操作,让$Splay$保持乱序,即“平衡”。
基本操作:
1.$pushup$/$pushdown$ 维护标记。$O(1)$
2.$rotate$ 上旋。$O(log n)$
3.$splay$ 将节点向上移动。$O(1)$
模板
1 #include<cstdio> 2 #define MAXL 100003 3 #define INF (1 << 30) 4 using namespace std; 5 class Splay { 6 #define root e[0].ch[1] 7 private: 8 class node { 9 public: 10 int v, father, ch[2], recy, sum; 11 } e[MAXL]; 12 int n, points; 13 inline void update(int x){ 14 e[x].sum = e[e[x].ch[0]].sum + e[e[x].ch[1]].sum + e[x].recy; 15 } 16 inline int identify(int x){ 17 return e[e[x].father].ch[1] == x; 18 } 19 inline void connect(int x, int f, int son){ 20 e[f].ch[son] = x; 21 e[x].father = f; 22 } 23 inline void rotate(int x){ 24 int y = e[x].father, mroot = e[y].father, mrootson = identify(y), yson = identify(x), 25 B = e[x].ch[yson ^ 1]; 26 connect(B, y, yson);connect(y, x, yson ^ 1);connect(x, mroot, mrootson); 27 update(y);update(x); 28 } 29 inline void splay(int at, int to){ 30 to = e[to].father; 31 while(e[at].father != to){ 32 int up = e[at].father; 33 if(e[up].father == to) rotate(at); 34 else if(identify(at) == identify(up)){ 35 rotate(up);rotate(at); 36 } else { 37 rotate(at);rotate(at); 38 } 39 } 40 } 41 inline int crepoint(int v, int father){ 42 n ++; 43 e[n].v = v; 44 e[n].father = father; 45 e[n].sum = e[n].recy = 1; 46 return n; 47 } 48 inline void destroy(int x){ 49 e[x].v = e[x].father = e[x].ch[0] = e[x].ch[1] = e[x].recy = e[x].sum = 0; 50 if(x == n) n --; 51 } 52 public: 53 inline int getroot(){return root;} 54 inline int find(int v){ 55 int now = root; 56 while(true){ 57 if(e[now].v == v){ 58 splay(now, root); 59 return now; 60 } 61 int nxt = e[now].v < v; 62 if(!e[now].ch[nxt]) return 0; 63 now = e[now].ch[nxt]; 64 } 65 } 66 inline int build(int v){ 67 if(!points) n = 0; 68 points ++; 69 if(!n){ 70 root = 1; 71 return crepoint(v, 0); 72 } 73 int now = root; 74 while(true){ 75 e[now].sum ++; 76 if(e[now].v == v){ 77 e[now].recy ++; 78 return now; 79 } 80 int nxt = e[now].v < v; 81 if(!e[now].ch[nxt]) return e[now].ch[nxt] = crepoint(v, now); 82 now = e[now].ch[nxt]; 83 } 84 return 0; 85 } 86 inline void push(int v){ 87 splay(build(v), root); 88 } 89 inline void pop(int v){ 90 int deal = find(v); 91 if(!deal) return; 92 points --; 93 if(e[deal].recy > 1){ 94 e[deal].recy --; 95 e[deal].sum --; 96 return; 97 } 98 if(!e[deal].ch[0]){ 99 root = e[deal].ch[1]; 100 e[root].father = 0; 101 } else { 102 int lef = e[deal].ch[0]; 103 while(e[lef].ch[1]) lef = e[lef].ch[1]; 104 splay(lef, e[deal].ch[0]); 105 int rig = e[deal].ch[1]; 106 connect(rig, lef, 1);connect(lef, 0, 1); 107 update(lef); 108 } 109 destroy(deal); 110 } 111 inline int rank(int v){ 112 int ans = 1, now = root; 113 while(true){ 114 if(e[now].v == v){ 115 ans += e[e[now].ch[0]].sum; 116 break; 117 } 118 if(!now) break; 119 if(v < e[now].v) now = e[now].ch[0]; 120 else { 121 ans += e[e[now].ch[0]].sum + e[now].recy; 122 now = e[now].ch[1]; 123 } 124 } 125 if(now) splay(now, root); 126 return ans; 127 } 128 inline int atrank(int x){ 129 if(x > points) return INF; 130 int now = root; 131 while(true){ 132 int minused = e[now].sum - e[e[now].ch[1]].sum; 133 if(x > e[e[now].ch[0]].sum && x <= minused) break; 134 if(x <= minused) now = e[now].ch[0]; 135 else { 136 x -= minused; 137 now = e[now].ch[1]; 138 } 139 } 140 splay(now, root); 141 return e[now].v; 142 } 143 inline int lower(int v){ 144 int now = root, res = -INF; 145 while(now){ 146 if(e[now].v < v && e[now].v > res) res = e[now].v; 147 if(e[now].v >= v) now = e[now].ch[0]; 148 else now = e[now].ch[1]; 149 } 150 return res; 151 } 152 inline int upper(int v){ 153 int now = root, res = INF; 154 while(now){ 155 if(e[now].v > v && e[now].v < res) res = e[now].v; 156 if(e[now].v > v) now = e[now].ch[0]; 157 else now = e[now].ch[1]; 158 } 159 return res; 160 } 161 } sp; 162 int n; 163 int main(){ 164 scanf("%d", &n); 165 while(n --){ 166 int opt, x; 167 scanf("%d%d", &opt, &x); 168 switch(opt){ 169 case 1: 170 sp.push(x); 171 break; 172 case 2: 173 sp.pop(x); 174 break; 175 case 3: 176 printf("%dn", sp.rank(x)); 177 break; 178 case 4: 179 printf("%dn", sp.atrank(x)); 180 break; 181 case 5: 182 printf("%dn", sp.lower(x)); 183 break; 184 case 6: 185 printf("%dn", sp.upper(x)); 186 } 187 } 188 }
因为$Splay$操作,$Splay$是一种非常强大的序列区间操作的数据结构。
1 // 文艺平衡树 2 // luogu-judger-enable-o2 3 #include<cstdio> 4 #include<algorithm> 5 #define MAXL 100010 6 #define Rint register int 7 using namespace std; 8 int n, m; 9 struct Splay { 10 #define root e[0].ch[1] 11 struct node { 12 int v, father, ch[2], sum, tag; 13 } e[MAXL]; 14 int n; 15 Splay(): n(0){} 16 inline void update(int x){ 17 e[x].sum = e[e[x].ch[0]].sum + e[e[x].ch[1]].sum + 1; 18 } 19 inline void pushdown(int x){ 20 if(x && e[x].tag){ 21 e[e[x].ch[0]].tag ^= 1; 22 e[e[x].ch[1]].tag ^= 1; 23 swap(e[x].ch[0], e[x].ch[1]); 24 e[x].tag = 0; 25 } 26 } 27 inline int identify(int x){ 28 return e[e[x].father].ch[1] == x; 29 } 30 inline int crepoint(int v, int f){ 31 e[++ n].v = v; 32 e[n].father = f; 33 e[n].sum = 1; 34 e[n].tag = 0; 35 return n; 36 } 37 inline void connect(int x, int f, int son){ 38 e[f].ch[son] = x; 39 e[x].father = f; 40 } 41 inline void rotate(int x){ 42 int y = e[x].father, mroot = e[y].father, mrootson = identify(y), yson = identify(x), 43 B = e[x].ch[yson ^ 1]; 44 connect(B, y, yson);connect(y, x, yson ^ 1);connect(x, mroot, mrootson); 45 update(y);update(x); 46 } 47 inline void splay(int at, int to){ 48 while(e[at].father != to){ 49 int up = e[at].father; 50 if(e[up].father == to) rotate(at); 51 else if(identify(at) == identify(up)){ 52 rotate(up);rotate(at); 53 } else { 54 rotate(at);rotate(at); 55 } 56 } 57 } 58 inline int build(int fa, int l, int r){ 59 if(l > r) return 0; 60 int mid = l + r >> 1, now = crepoint(mid, fa); 61 if(!root) root = now; 62 e[now].ch[0] = build(now, l, mid - 1); 63 e[now].ch[1] = build(now, mid + 1, r); 64 update(now); 65 return now; 66 } 67 inline int atrank(int x){ 68 int now = root; 69 while(true){ 70 pushdown(now); 71 int minused = e[e[now].ch[0]].sum + 1; 72 if(x == minused) return now; 73 if(x < minused) now = e[now].ch[0]; 74 else { 75 x -= minused; 76 now = e[now].ch[1]; 77 } 78 } 79 } 80 inline void rev(int l, int r){ 81 l = atrank(l); 82 r = atrank(r + 2); 83 splay(l, 0); 84 splay(r, l); 85 e[e[e[root].ch[1]].ch[0]].tag ^= 1; 86 } 87 inline void write(int x){ 88 pushdown(x); 89 if(e[x].ch[0]) write(e[x].ch[0]); 90 if(e[x].v > 1 && e[x].v < ::n + 2) printf("%d ", e[x].v - 1); 91 if(e[x].ch[1]) write(e[x].ch[1]); 92 } 93 } sp; 94 int main(){ 95 scanf("%d%d", &n, &m); 96 sp.build(0, 1, n + 2); 97 while(m --){ 98 int l, r; 99 scanf("%d%d", &l, &r); 100 sp.rev(l, r); 101 } 102 sp.write(sp.root); 103 }
2.替罪羊树
替罪羊树有一个平衡因子$alpha$,一般取0.75~0.8时最佳。
每次在更新和查询的时候,对于节点$x$,若$max(siz_{ls},siz_{rs})>siz_x*alpha$,则对以$x$为根的子树进行拍扁重构。
拍扁指的是将排序的数组,即中序遍历记录到一个数组中,然后在对这个子树进行建树,建树的时候选择中点作为根,这颗子树就平衡了。
因为经常需要拍扁重构,所以最好使用垃圾回收,用一个内存池,使用一个栈或队列记录空余空间,这样就不会担心$MLE$了。
#include<cstdio> #define alpha 0.8 #define MAXN 100003 #define Rint register int using namespace std; int n, top, goat, rt, t1, ls[MAXN], rs[MAXN], siz[MAXN], v[MAXN], tmp[MAXN], cnt[MAXN], stk[MAXN]; inline int max(int a, int b){return a < b ? b : a;} inline void dfs(int root){ if(root){ dfs(ls[root]); if(exist[root]) tmp[++ t1] = root; else stk[top ++] = root; dfs(rs[root]); } } inline void build(int &root, int L, int R){ int mid = L + R >> 1; root = tmp[mid]; if(L == R){ siz[root] = cnt[root] = 1; ls[root] = rs[root] = 0; exist[root] = true; return; } if(L < mid) build(ls[root], L, mid - 1); else ls[root] = 0; build(rs[root], mid + 1, R); siz[root] = siz[ls[root]] + siz[rs[root]] + 1; cnt[root] = cnt[ls[root]] + cnt[rs[root]] + 1; } inline void rebuild(int &root){ t1 = 0; dfs(root); if(t1) build(root, 1, t1); else root = 0; } inline int rank(int x){ int now = rt, ans = 1; while(now){ if(x <= v[now]) now = ls[now]; else { ans += siz[ls[now]] + exist[now]; now = rs[now]; } } return ans; } inline void insert(int &root, int x){ if(!root){ root = stk[-- top]; v[root] = x; siz[root] = cnt[root] = 1; exist[root] = true; ls[root] = rs[root] = 0; return; } siz[root] ++;cnt[root] ++; if(x <= v[root]) insert(ls[root], x); else insert(rs[root], x); if(siz[root] * alpha < max(siz[ls[root]], siz[rs[root]])){ if(goat){ if(goat == ls[root]) rebuild(ls[root]); else rebuild(rs[root]); goat = 0; } } else goat = root; } inline void del_id(int root, int x){ siz[root] --; if(exist[root] && x == siz[ls[root]] + 1){ exist[root] = false; return; } if(x <= siz[ls[root]] + exist[root]) del_id(ls[root], x); else del_id(rs[root], x - siz[ls[root]] - exist[root]); } inline void del_val(int x){ del_id(rt, rank(x)); if(siz[rt] < alpha * cnt[rt]) rebuild(rt); } inline int get_xth(int x){ int now = rt; while(now){ if(exist[now] && x == siz[ls[now]] + 1) return v[now]; else if(x <= siz[ls[now]] + exist[now]) now = ls[now]; else{ x -= siz[ls[now]] + exist[now]; now = rs[now]; } } } int main(){ scanf("%d", &n); rt = 0; for(Rint i = 1;i <= 100000;i ++) stk[top ++] = i; for(Rint i = 1;i <= n;i ++){ int opt, x; scanf("%d%d", &opt, &x); switch(opt){ case 1: insert(rt, x); break; case 2: del_val(x); break; case 3: printf("%dn", rank(x)); break; case 4: printf("%dn", get_xth(x)); break; case 5: printf("%dn", get_xth(rank(x) - 1)); break; case 6: printf("%dn", get_xth(rank(x + 1))); } } }
3.$Treap$
$Treap=Tree+Heap$,这种平衡树利用堆的平衡性质,来维持整个二叉排序树平衡。
每个节点有一个$key$和$prio$值,$key$是维护的集合的数值,$prio$是随机的优先级。
我们保证每一时刻整棵树对$key$是一棵二叉排序树,对$prio$是一个二叉堆。
如果遇到破坏堆性质的情况,使用$rotate$来维持二叉堆的性质。
1 inline void rotate(Treap *&x, int d){ 2 Treap *y = x -> ch[d ^ 1]; 3 if(y == null) return; 4 x -> ch[d ^ 1] = y -> ch[d]; 5 y -> ch[d] = x; 6 y -> size = x -> size; 7 update(x); 8 x = y; 9 }
普通平衡树
1 #include<cstdio> 2 #define MAXN 100003 3 #define INF 2147483647 4 using namespace std; 5 struct Treap { 6 int key, prio, size, cnt; 7 Treap *ch[2]; 8 } *null, *top, *root; 9 inline int rand(){ 10 static int seed = 1475369; 11 return seed = seed * 789456741LL % INF; 12 } 13 inline void init(){ 14 null = top = root = new Treap[MAXN]; 15 null -> ch[0] = null -> ch[1] = null; 16 null -> key = null -> prio = INF; 17 null -> size = null -> cnt = 0; 18 } 19 inline void update(Treap *&x){ 20 x -> size = x -> ch[0] -> size + x -> ch[1] -> size + x -> cnt; 21 } 22 inline void rotate(Treap *&x, int d){ 23 Treap *y = x -> ch[d ^ 1]; 24 if(y == null) return; 25 x -> ch[d ^ 1] = y -> ch[d]; 26 y -> ch[d] = x; 27 y -> size = x -> size; 28 update(x); 29 x = y; 30 } 31 inline Treap* newnode(int key){ 32 top ++; 33 top -> ch[0] = top -> ch[1] = null; 34 top -> key = key; 35 top -> prio = rand(); 36 top -> size = top -> cnt = 1; 37 return top; 38 } 39 void insert(Treap *&x, int key){ 40 if(x == null){x = newnode(key); return;} 41 if(x -> key == key) x -> cnt ++; 42 else { 43 int d = key > x -> key; 44 insert(x -> ch[d], key); 45 if(x -> prio > x -> ch[d] -> prio) rotate(x, d ^ 1); 46 } 47 update(x); 48 } 49 void erase(Treap *&x, int key){ 50 if(x == null) return; 51 if(x -> key == key){ 52 if(x -> cnt > 1){x -> cnt --; update(x); return;} 53 if(x -> ch[0] == null) x = x -> ch[1]; 54 else if(x -> ch[1] == null) x = x -> ch[0]; 55 else { 56 int d = x -> ch[0] -> prio < x -> ch[1] -> prio; 57 rotate(x, d); 58 erase(x -> ch[d], key); 59 } 60 } else erase(x -> ch[key > x -> key], key); 61 update(x); 62 } 63 inline int rank(int key){ 64 int ans = 1; 65 Treap *now = root; 66 while(true){ 67 if(now == null) return ans; 68 if(now -> key == key) 69 return ans + now -> ch[0] -> size; 70 else if(now -> key > key) 71 now = now -> ch[0]; 72 else { 73 ans += now -> ch[0] -> size + now -> cnt; 74 now = now -> ch[1]; 75 } 76 } 77 } 78 inline int atrank(int x){ 79 Treap *now = root; 80 while(true){ 81 int minused = now -> size - now -> ch[1] -> size; 82 if(x > now -> ch[0] -> size && x <= minused) return now -> key; 83 if(x <= now -> ch[0] -> size) now = now -> ch[0]; 84 else { 85 x -= minused; 86 now = now -> ch[1]; 87 } 88 } 89 } 90 inline int lower(int key){ 91 Treap *now = root; 92 int ans = -INF; 93 while(now != null){ 94 if(now -> key < key && now -> key > ans) ans = now -> key; 95 if(now -> key >= key) now = now -> ch[0]; 96 else now = now -> ch[1]; 97 } 98 return ans; 99 } 100 inline int upper(int key){ 101 Treap *now = root; 102 int ans = INF; 103 while(now != null){ 104 if(now -> key > key && now -> key < ans) ans = now -> key; 105 if(now -> key > key) now = now -> ch[0]; 106 else now = now -> ch[1]; 107 } 108 return ans; 109 } 110 int n; 111 int main(){ 112 // freopen("Treap.in", "r", stdin); 113 // freopen("Treap.out", "w", stdout); 114 scanf("%d", &n); 115 init(); 116 while(n --){ 117 int opt, x; 118 scanf("%d%d", &opt, &x); 119 switch(opt){ 120 case 1: 121 insert(root, x); 122 break; 123 case 2: 124 erase(root, x); 125 break; 126 case 3: 127 printf("%dn", rank(x)); 128 break; 129 case 4: 130 printf("%dn", atrank(x)); 131 break; 132 case 5: 133 printf("%dn", lower(x)); 134 break; 135 case 6: 136 printf("%dn", upper(x)); 137 } 138 } 139 }
4.$fhq treap$
$fhq treap$和$treap$一样,使用随机权值来保持随机性质。
但它不是依靠$rotate$操作,而是分裂$split$和合并$merge$操作。
$split$将整棵树分为两部分,其中一部分的最大值小于另一部分的最小值。
$merge$是$split$的逆操作,需要一部分的最大值小于另一部分的最小值。
1 inline void split(int now, int k, int &x, int &y){ 2 if(!now){ 3 x = y = 0; 4 return; 5 } 6 pushdown(now); 7 if(e[e[now].ch[0]].size < k){ 8 x = now; split(e[now].ch[1], k - e[e[now].ch[0]].size - 1, e[x].ch[1], y); 9 } else { 10 y = now; split(e[now].ch[0], k, x, e[y].ch[0]); 11 } 12 pushup(now); 13 } 14 inline void merge(int &now, int x, int y){ 15 if(!x || !y){ 16 now = x ^ y; 17 return; 18 } 19 if(e[x].prio < e[y].prio){ 20 pushdown(x); 21 now = x; merge(e[now].ch[1], e[x].ch[1], y); 22 } else { 23 pushdown(y); 24 now = y; merge(e[now].ch[0], x, e[y].ch[0]); 25 } 26 pushup(now); 27 }
普通平衡树
1 // luogu-judger-enable-o2 2 #include<cstdio> 3 #define Rint register int 4 using namespace std; 5 const int N = 100003, INF = 1000000007; 6 int n; 7 inline int rand(){ 8 static int seed = 20050915; 9 return seed = seed * 19260817ll % 2147483647; 10 } 11 struct Node { 12 int key, prio, size, ch[2]; 13 } e[N]; 14 int tot = 0, root = 1; 15 inline int newnode(int key = 0){ 16 ++ tot; 17 e[tot].key = key; 18 e[tot].prio = rand(); 19 e[tot].size = 1; 20 return tot; 21 } 22 inline void pushup(int x){ 23 e[x].size = e[e[x].ch[0]].size + e[e[x].ch[1]].size + 1; 24 } 25 inline void split(int now, int key, int &x, int &y){ 26 if(!now){ 27 x = y = 0; 28 return; 29 } 30 if(e[now].key <= key){ 31 x = now; split(e[now].ch[1], key, e[x].ch[1], y); 32 } else { 33 y = now; split(e[now].ch[0], key, x, e[y].ch[0]); 34 } 35 pushup(now); 36 } 37 inline void merge(int &now, int x, int y){ 38 if(!x || !y){ 39 now = x + y; 40 return; 41 } 42 if(e[x].prio < e[y].prio){ 43 now = x; merge(e[now].ch[1], e[x].ch[1], y); 44 } else { 45 now = y; merge(e[now].ch[0], x, e[y].ch[0]); 46 } 47 pushup(now); 48 } 49 inline void insert(int key){ 50 int x = 0, y = 0, z = newnode(key); 51 split(root, key, x, y); 52 merge(x, x, z); 53 merge(root, x, y); 54 } 55 inline void erase(int key){ 56 int x = 0, y = 0, z = 0; 57 split(root, key, x, y); 58 split(x, key - 1, x, z); 59 merge(z, e[z].ch[0], e[z].ch[1]); 60 merge(x, x, z); 61 merge(root, x, y); 62 } 63 inline int rank(int key){ 64 int x = 0, y = 0; 65 split(root, key - 1, x, y); 66 int ans = e[x].size + 1; 67 merge(root, x, y); 68 return ans; 69 } 70 inline int find(int now, int x){ 71 while(true){ 72 int minused = e[e[now].ch[0]].size + 1; 73 if(x == minused) return e[now].key; 74 if(x < minused) now = e[now].ch[0]; 75 else { 76 x -= minused; 77 now = e[now].ch[1]; 78 } 79 } 80 } 81 inline int lower(int key){ 82 int x = 0, y = 0; 83 split(root, key - 1, x, y); 84 int ans = find(x, e[x].size); 85 merge(root, x, y); 86 return ans; 87 } 88 inline int upper(int key){ 89 int x = 0, y = 0; 90 split(root, key, x, y); 91 int ans = find(y, 1); 92 merge(root, x, y); 93 return ans; 94 } 95 int main(){ 96 scanf("%d", &n); 97 newnode(2147483647); 98 e[1].size = 0; 99 while(n --){ 100 int opt, x; 101 scanf("%d%d", &opt, &x); 102 switch(opt){ 103 case 1: 104 insert(x); 105 break; 106 case 2: 107 erase(x); 108 break; 109 case 3: 110 printf("%dn", rank(x)); 111 break; 112 case 4: 113 printf("%dn", find(root, x)); 114 break; 115 case 5: 116 printf("%dn", lower(x)); 117 break; 118 case 6: 119 printf("%dn", upper(x)); 120 } 121 } 122 }
因为$fhq treap$可以对树进行分裂合并,所以$fhq treap$可以用来维护序列问题,而且有时候比$splay$要方便得多。
文艺平衡树
1 // luogu-judger-enable-o2 2 #include<cstdio> 3 #include<algorithm> 4 #define Rint register int 5 using namespace std; 6 const int N = 100003, INF = 2147483647; 7 int n, m; 8 inline int rand(){ 9 static int seed = 20050915; 10 return seed = seed * 19260817ll % INF; 11 } 12 struct Node { 13 int key, prio, ch[2], size; 14 bool rev; 15 } e[N]; 16 int root, tot = 0; 17 inline int newnode(int key = 0){ 18 tot ++; 19 e[tot].key = key; e[tot].prio = rand(); e[tot].size = 1; 20 e[tot].ch[0] = e[tot].ch[1] = e[tot].rev = 0; 21 return tot; 22 } 23 inline void pushup(int x){ 24 e[x].size = e[e[x].ch[0]].size + e[e[x].ch[1]].size + 1; 25 } 26 inline void pushdown(int x){ 27 if(x && e[x].rev){ 28 swap(e[x].ch[0], e[x].ch[1]); 29 e[e[x].ch[0]].rev ^= 1; 30 e[e[x].ch[1]].rev ^= 1; 31 e[x].rev = false; 32 } 33 } 34 inline void split(int now, int k, int &x, int &y){ 35 if(!now){ 36 x = y = 0; 37 return; 38 } 39 pushdown(now); 40 if(e[e[now].ch[0]].size < k){ 41 x = now; split(e[now].ch[1], k - e[e[now].ch[0]].size - 1, e[x].ch[1], y); 42 } else { 43 y = now; split(e[now].ch[0], k, x, e[y].ch[0]); 44 } 45 pushup(now); 46 } 47 inline void merge(int &now, int x, int y){ 48 if(!x || !y){ 49 now = x ^ y; 50 return; 51 } 52 if(e[x].prio < e[y].prio){ 53 pushdown(x); 54 now = x; merge(e[now].ch[1], e[x].ch[1], y); 55 } else { 56 pushdown(y); 57 now = y; merge(e[now].ch[0], x, e[y].ch[0]); 58 } 59 pushup(now); 60 } 61 inline void rever(int l, int r){ 62 int a = 0, b = 0, c = 0; 63 split(root, l - 1, a, b); 64 split(b, r - l + 1, b, c); 65 e[b].rev ^= 1; 66 merge(b, b, c); 67 merge(root, a, b); 68 } 69 inline void write(int x){ 70 if(!x) return; 71 pushdown(x); 72 write(e[x].ch[0]); 73 printf("%d ", e[x].key); 74 write(e[x].ch[1]); 75 } 76 int main(){ 77 scanf("%d%d", &n, &m); 78 root = newnode(1); 79 for(Rint i = 2;i <= n;i ++) 80 merge(root, root, newnode(i)); 81 while(m --){ 82 int l, r; 83 scanf("%d%d", &l, &r); 84 rever(l, r); 85 } 86 write(root); 87 }
因为$fhq treap$进行的分裂合并操作,不会大幅度改变树的形态,所以可以进行可持久化。
可持久化平衡树
1 #include<cstdio> 2 #define Rint register int 3 using namespace std; 4 const int N = 500003, INF = 2147483647; 5 inline int rand(){ 6 static int seed = 20050915; 7 return seed = seed * 19820605ll % INF; 8 } 9 struct Node { 10 int key, prio, ch[2], size; 11 } e[N * 50]; 12 int tot = 0, root[N]; 13 inline int newnode(int key = 0){ 14 e[++ tot].key = key; e[tot].prio = rand(); 15 e[tot].size = 1; e[tot].ch[0] = e[tot].ch[1] = 0; 16 return tot; 17 } 18 inline void pushup(int x){ 19 e[x].size = e[e[x].ch[0]].size + e[e[x].ch[1]].size + 1; 20 } 21 inline int merge(int x, int y){ 22 if(!x || !y) return x + y; 23 if(e[x].prio < e[y].prio){ 24 int now = ++ tot; e[now] = e[x]; 25 e[now].ch[1] = merge(e[x].ch[1], y); 26 pushup(now); return now; 27 } 28 int now = ++ tot; e[now] = e[y]; 29 e[now].ch[0] = merge(x, e[y].ch[0]); 30 pushup(now); return now; 31 } 32 inline void split(int now, int key, int &x, int &y){ 33 if(!now){ 34 x = y = 0; 35 return; 36 } 37 if(e[now].key <= key){ 38 x = ++ tot; e[x] = e[now]; split(e[now].ch[1], key, e[x].ch[1], y); pushup(x); 39 } else { 40 y = ++ tot; e[y] = e[now]; split(e[now].ch[0], key, x, e[y].ch[0]); pushup(y); 41 } 42 } 43 inline void insert(int &root, int key){ 44 int x = 0, y = 0, z = newnode(key); 45 split(root, key, x, y); 46 root = merge(merge(x, z), y); 47 } 48 inline void erase(int &root, int key){ 49 int x = 0, y = 0, z = newnode(key); 50 split(root, key, x, y); 51 split(x, key - 1, x, z); 52 z = merge(e[z].ch[0], e[z].ch[1]); 53 root = merge(merge(x, z), y); 54 } 55 inline int rank(int &root, int key){ 56 int x = 0, y = 0; 57 split(root, key - 1, x, y); 58 int ans = e[x].size + 1; 59 root = merge(x, y); 60 return ans; 61 } 62 inline int atrank(int now, int k){ 63 if(k == e[e[now].ch[0]].size + 1) return e[now].key; 64 if(k <= e[e[now].ch[0]].size) return atrank(e[now].ch[0], k); 65 return atrank(e[now].ch[1], k - e[e[now].ch[0]].size - 1); 66 } 67 inline int lower(int &root, int key){ 68 int x = 0, y = 0; 69 split(root, key - 1, x, y); 70 int ans = atrank(x, e[x].size); 71 root = merge(x, y); 72 return ans; 73 } 74 inline int upper(int &root, int key){ 75 int x = 0, y = 0; 76 split(root, key, x, y); 77 int ans = atrank(y, 1); 78 root = merge(x, y); 79 return ans; 80 } 81 int n; 82 int main(){ 83 scanf("%d", &n); 84 for(Rint i = 1;i <= n;i ++){ 85 int tim, opt, x; 86 scanf("%d%d%d", &tim, &opt, &x); 87 root[i] = root[tim]; 88 switch(opt){ 89 case 1: 90 insert(root[i], x); break; 91 case 2: 92 erase(root[i], x); break; 93 case 3: 94 printf("%dn", rank(root[i], x)); break; 95 case 4: 96 printf("%dn", atrank(root[i], x)); break; 97 case 5: 98 printf("%dn", lower(root[i], x)); break; 99 case 6: 100 printf("%dn", upper(root[i], x)); break; 101 } 102 } 103 }
可持久化文艺平衡树
1 // luogu-judger-enable-o2 2 #include<cstdio> 3 #include<algorithm> 4 #define Rint register int 5 using namespace std; 6 typedef long long LL; 7 const int N = 220003, INF = 2147483647; 8 inline int rand(){ 9 static int seed = 20050915; 10 return seed = ((seed * 19260817ll % 998244353) ^ 20050818ll)% INF; 11 } 12 struct Node { 13 int key, prio, ch[2], size; 14 LL sum; 15 bool rev; 16 } e[N * 70]; 17 int tot, root[N]; 18 inline void pushup(int x){ 19 e[x].size = e[e[x].ch[0]].size + e[e[x].ch[1]].size + 1; 20 e[x].sum = e[e[x].ch[0]].sum + e[e[x].ch[1]].sum + e[x].key; 21 } 22 inline void pushdown(int x){ 23 if(x && e[x].rev){ 24 if(e[x].ch[0]){ 25 int now = ++ tot; 26 e[now] = e[e[x].ch[0]]; 27 e[now].rev ^= 1; 28 e[x].ch[0] = now; 29 } 30 if(e[x].ch[1]){ 31 int now = ++ tot; 32 e[now] = e[e[x].ch[1]]; 33 e[now].rev ^= 1; 34 e[x].ch[1] = now; 35 } 36 swap(e[x].ch[0], e[x].ch[1]); 37 e[x].rev = false; 38 } 39 } 40 inline int crepoint(int key){ 41 tot ++; 42 e[tot].key = e[tot].sum = key; 43 e[tot].ch[0] = e[tot].ch[1] = e[tot].rev = 0; 44 e[tot].size = 1; 45 e[tot].prio = rand(); 46 return tot; 47 } 48 inline int merge(int x, int y){ 49 if(!x || !y) return x + y; 50 if(e[x].prio < e[y].prio){ 51 pushdown(x); 52 e[x].ch[1] = merge(e[x].ch[1], y); 53 pushup(x); 54 return x; 55 } else { 56 pushdown(y); 57 e[y].ch[0] = merge(x, e[y].ch[0]); 58 pushup(y); 59 return y; 60 } 61 } 62 inline void split(int now, int k, int &x, int &y){ 63 if(!now){ 64 x = y = 0; 65 return; 66 } 67 pushdown(now); 68 if(e[e[now].ch[0]].size < k){ 69 x = ++ tot; e[x] = e[now]; split(e[now].ch[1], k - e[e[now].ch[0]].size - 1, e[x].ch[1], y); pushup(x); 70 } else { 71 y = ++ tot; e[y] = e[now]; split(e[now].ch[0], k, x, e[y].ch[0]); pushup(y); 72 } 73 }inline void insert(int &root, int pos, int key){ 74 int x = 0, y = 0; 75 split(root, pos, x, y); 76 root = merge(x, merge(crepoint(key), y)); 77 } 78 inline void erase(int &root, int pos){ 79 int x = 0, y = 0, z = 0; 80 split(root, pos - 1, x, y); 81 split(y, 1, y, z); 82 root = merge(x, z); 83 } 84 inline void rever(int &root, int l, int r){ 85 int a = 0, b = 0, c = 0; 86 split(root, l - 1, a, b); 87 split(b, r - l + 1, b, c); 88 e[b].rev ^= 1; 89 root = merge(a, merge(b, c)); 90 } 91 inline LL query(int &root, int l, int r){ 92 int a = 0, b = 0, c = 0; 93 split(root, l - 1, a, b); 94 split(b, r - l + 1, b, c); 95 LL ans = e[b].sum; 96 root = merge(a, merge(b, c)); 97 return ans; 98 } 99 int n; 100 LL lastans = 0; 101 int main(){ 102 scanf("%d", &n); 103 for(Rint i = 1;i <= n;i ++){ 104 int opt; 105 LL v, x, y; 106 scanf("%lld%d%lld", &v, &opt, &x); 107 x ^= lastans; 108 if(opt != 2) scanf("%lld", &y), y ^= lastans; 109 root[i] = root[v]; 110 switch(opt){ 111 case 1: 112 insert(root[i], x, y); 113 break; 114 case 2: 115 erase(root[i], x); 116 break; 117 case 3: 118 rever(root[i], x, y); 119 break; 120 case 4: 121 printf("%lldn", lastans = query(root[i], x, y)); 122 } 123 } 124 }
注意可持久化$fhq treap$中,对于$pushup$和$pushdown$是有一定区别的。
注意其中一些细微的差异,比如普通版本的split只要update(x)即可,而可持久化版本的要对r或l而非x调用update,因为x的孩子并没有被修改 另外,关于merge,理论上来讲merge里也应该克隆结点,但是本题并不需要,因为我们在调用merge之前总是会调用split,就已经把该克隆的结点克隆完了。 ———— GKxx dalao
【剩下的坑以后再补】
- 还没有人评论,欢迎说说您的想法!