标签:
一、斜堆
斜堆是一种可以合并的堆
节点信息:
struct Node { int v; Node *ch[2]; };
主要利用merge函数
Node *merge(Node *x, Node *y) { if(!x) return y; if(!y) return x; if(x->v < y->v) swap(x, y); x->ch[1] = merge(x->ch[1], y); return swap(x->ch[0], x->ch[1]), x; }
左偏树需要维护一个额外的信息,而斜堆每次强制swap(ch[0], ch[1]),以达到均摊O(logn)的效果
利用merge函数可以很容易地实现插入和删除
void ins(Node*& o, int x) { o = merge(o, new Node(x)); } void del(Node*& o) { o = merge(o->ch[0], o->ch[1]); }
另外地,堆相对与平衡树来说无法删除一个元素,但是如果能够定位到这个指针就可以删除这个元素了,方法是存储父亲(merge里要维护一下fa)
删除一个元素时可以这样
void del(Node*& o) { Node* p = o->fa; Node* r = merge(o->ch[0], o->ch[1]); r->fa = p; p->ch[p->ch[1] == o] = r; }
当然,和其他树形结构一样,堆上也是可以维护tag的
加入maintain()函数和down()函数后就可以轻松实现这些功能,方法和其他树形结构类似
需要注意的是删除时要像自底向上的splay一样把从这个点到根的路径上的点都down()一遍,再调用del()函数
另外,由于堆高度是期望O(logn)的,所以在处理集合信息时不需要额外的并查集,只需要每次暴力往上找到根来比较就能够做到O(logn)的复杂度了。
举一例带标记的题
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstdlib> 5 #include<cstring> 6 #include<string> 7 8 using namespace std; 9 10 void setIO(const string& a) { 11 freopen((a+".in").c_str(), "r", stdin); 12 freopen((a+".out").c_str(), "w", stdout); 13 } 14 15 typedef long long ll; 16 const int N = 300000 + 10; 17 18 struct Node* pis; 19 struct Node { 20 ll v, a, m; 21 int id, fk, tag; 22 Node* ch[2]; 23 24 void add_tag(ll add, ll mul, int ft) { 25 if(this) { 26 fk += ft; 27 tag += ft; 28 (v *= mul) += add; 29 m *= mul; 30 (a *= mul) += add; 31 } 32 } 33 34 void down() { 35 ch[0]->add_tag(a, m, tag); 36 ch[1]->add_tag(a, m, tag); 37 tag = a = 0, m = 1; 38 } 39 40 void *operator new(size_t) { 41 return pis++; 42 } 43 44 Node(ll v = 0, int id = 0) : v(v), id(id) { 45 ch[0] = ch[1] = 0; 46 a = 0; 47 m = 1; 48 fk = tag = 0; 49 } 50 }pool[N], *root[N]; 51 52 Node *merge(Node *l, Node *r) { 53 if(!l || !r) return l ? l : r; 54 if(l->v > r->v) swap(l, r); 55 l->down(); 56 l->ch[1] = merge(l->ch[1], r); 57 return swap(l->ch[0], l->ch[1]), l; 58 } 59 60 ll h[N], f[N], a[N], v[N]; 61 int ansn[N], ansm[N]; 62 63 template<typename Q> void gt(Q& x) { 64 static char c; 65 static bool f; 66 for(f = 0; c = getchar(), !isdigit(c); ) if(c == ‘-‘) f = 1; 67 for(x = 0; isdigit(c); c = getchar()) x = x * 10 + c - ‘0‘; 68 if(f) x = -x; 69 } 70 71 void ddd(Node* o) { 72 if(!o) return; 73 o->down(); 74 ddd(o->ch[0]); 75 ddd(o->ch[1]); 76 } 77 78 int main() { 79 #ifdef DEBUG 80 freopen("in.txt", "r", stdin); 81 freopen("out.txt", "w", stdout); 82 #endif 83 84 pis = pool; 85 int n, m; 86 gt(n), gt(m); 87 for(int i = 1; i <= n; i++) { 88 gt(h[i]); 89 } 90 for(int i = 2; i <= n; i++) { 91 gt(f[i]), gt(a[i]), gt(v[i]); 92 } 93 for(int i = 1; i <= m; i++) { 94 ll s, c; 95 gt(s), gt(c); 96 root[c] = merge(root[c], new Node(s, i)); 97 } 98 99 for(int i = n; i >= 1; i--) { 100 Node*& r = root[i]; 101 while(r && r->v < h[i]) { 102 ansn[i]++; 103 r->down(); 104 r = merge(r->ch[0], r->ch[1]); 105 } 106 ll add = 0, mul = 1; 107 if(a[i] == 0) add += v[i]; 108 else mul *= v[i]; 109 r->add_tag(add, mul, 1); 110 root[f[i]] = merge(root[f[i]], r); 111 } 112 113 for(int i = 1; i <= n; i++) { 114 printf("%d\n", ansn[i]); 115 } 116 ddd(root[0]); 117 for(int i = 0; i < m; i++) { 118 ansm[pool[i].id] = pool[i].fk; 119 } 120 for(int i = 1; i <= m; i++) { 121 printf("%d\n", ansm[i]); 122 } 123 124 return 0; 125 }
二、不旋转的treap
参见 http://memphis.is-programmer.com/posts/46317.html
这种treap的实现以分裂(split)和合并(merge)操作为主体,可以解决一些区间问题,比如区间反转。
merge()的过程和斜堆的merge()很像。
其中笛卡尔建树据说是很有用的东西
附tyvj1729文艺平衡树代码,稍作修改就可以实现可持久化
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstdlib> 5 #include<cstring> 6 #include<string> 7 8 using namespace std; 9 10 void setIO(const string& a) { 11 freopen((a+".in").c_str(), "r", stdin); 12 freopen((a+".out").c_str(), "w", stdout); 13 } 14 15 const int N = 100000 + 10; 16 17 #define size(x) ((x) ? (x)->sz : 0) 18 19 struct Node *pis; 20 21 struct Node { 22 int fix, v, sz; 23 bool flip; 24 Node *l, *r; 25 Node(int v = 0) : v(v) { 26 fix = rand(); 27 l = r = 0; 28 sz = 1; 29 flip = 0; 30 } 31 void maintain() { 32 sz = size(l) + size(r) + 1; 33 } 34 void down() { 35 if(flip) { 36 swap(l, r); 37 if(l) l->flip ^= 1; 38 if(r) r->flip ^= 1; 39 flip = 0; 40 } 41 } 42 43 void* operator new(size_t) { 44 return pis++; 45 } 46 }pool[N], *root; 47 Node *merge(Node *x, Node *y) { 48 if(!x) return y; 49 if(!y) return x; 50 if(x->fix < y->fix) { 51 x->down(); 52 x->r = merge(x->r, y); 53 return x->maintain(), x; 54 }else { 55 y->down(); 56 y->l = merge(x, y->l); 57 return y->maintain(), y; 58 } 59 } 60 61 typedef pair<Node*, Node*> Root; 62 63 Root split(Node *x, int k) { 64 if(!x) return Root(0, 0); 65 Root y; x->down(); 66 if(size(x->l) >= k) { 67 y = split(x->l, k); 68 x->l = y.second; 69 y.second = x; 70 }else { 71 y = split(x->r, k - size(x->l) - 1); 72 x->r = y.first; 73 y.first = x; 74 } 75 return x->maintain(), y; 76 } 77 78 Node *build(int n) { 79 static Node *stk[N], *x, *last; 80 int p = 0; 81 for(int i = 0; i <= n; i++) { 82 x = new Node(i); 83 last = 0; 84 while(p && stk[p]->fix > x->fix) { 85 stk[p]->maintain(); 86 last = stk[p--]; 87 } 88 if(p) stk[p]->r = x; 89 x->l = last; 90 stk[++p] = x; 91 } 92 while(p) stk[p--]->maintain(); 93 return stk[1]; 94 } 95 96 int n; 97 98 void print(Node *o) { 99 if(!o) return; 100 o->down(); 101 print(o->l); 102 if(0 < o->v && o->v <= n) printf("%d ", o->v); 103 print(o->r); 104 } 105 106 int main() { 107 #ifdef DEBUG 108 freopen("in.txt", "r", stdin); 109 // freopen("out.txt", "w", stdout); 110 #endif 111 112 pis = pool; 113 int m; 114 scanf("%d%d", &n, &m); 115 root = build(n + 1); 116 while(m--) { 117 int l, r; 118 scanf("%d%d", &l, &r); 119 Root x = split(root, l); 120 Root y = split(x.second, r - l + 1); 121 y.first->flip ^= 1; 122 root = merge(merge(x.first, y.first), y.second); 123 } 124 print(root); 125 126 return 0; 127 }
三、替罪羊树
说到不用旋转的平衡树,自然还有替罪羊树
之前好像有一种O(n ** 1.5)的朝鲜树在树高为O(n ** 0.5)时重建整棵树
而替罪羊树在是在插入时检查如果某一个子树的左子树或右子树的大小超过该子树的55%~80%的时候重建这颗子树
然后就是O(nlogn)的复杂度了...
值得一提的是删除
一种方法是删除时打上删除标记,然后不管(sz信息要变成0),等到下次重建这个点的时候把这个点扔掉
还有一种没有复杂度保证但是很好写的,就是重建改点的左右子树。
附暴力重建子树的代码
tyvj1728普通平衡树
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstdlib> 5 #include<cstring> 6 #include<string> 7 8 using namespace std; 9 10 void setIO(const string& a) { 11 freopen((a+".in").c_str(), "r", stdin); 12 freopen((a+".out").c_str(), "w", stdout); 13 } 14 15 #define szof(x) ((x) ? (x)->sz : 0) 16 17 const int N = 200000 + 10; 18 19 struct Node *pis; 20 struct Node { 21 int v, sz; 22 Node *ch[2]; 23 24 Node(int v = 0) : v(v) { 25 sz = 1; 26 ch[0] = ch[1] = 0; 27 } 28 29 void maintain() { 30 sz = szof(ch[0]) + szof(ch[1]) + 1; 31 } 32 33 int cmp(int x) const { 34 if(x == v) return -1; 35 return x < v ? 0 : 1; 36 } 37 38 void *operator new(size_t) { 39 return pis++; 40 } 41 }pool[N], *cache[N], *root; 42 int tot = 0; 43 44 void print(Node *o) { 45 if(!o) return; 46 print(o->ch[0]); 47 cache[++tot] = o; 48 print(o->ch[1]); 49 } 50 51 void rebuild(Node*& o, int l, int r) { 52 if(l > r) return o = 0, void(); 53 int mid = (l + r) >> 1; 54 o = cache[mid]; 55 rebuild(o->ch[0], l, mid - 1); 56 rebuild(o->ch[1], mid + 1, r); 57 o->maintain(); 58 } 59 60 void insert(Node*& o, int x) { 61 if(!o) o = new Node(x); 62 else { 63 int d = o->cmp(x); 64 if(d == -1) d = 0; 65 insert(o->ch[d], x); 66 } 67 o->maintain(); 68 } 69 70 void scape(Node*& o, int x) { 71 int d = o->cmp(x); 72 if(d == -1) return; 73 if(o->ch[d]->sz > o->sz * 0.75) { 74 tot = 0; 75 print(o); 76 rebuild(o, 1, tot); 77 }else scape(o->ch[d], x); 78 } 79 80 void insert(int x) { 81 insert(root, x); 82 scape(root, x); 83 } 84 85 void remove(Node*& o, int x) { 86 int d = o->cmp(x); 87 if(d == -1) { 88 if(!o->ch[0] && !o->ch[1]) o = 0; 89 else { 90 tot = 0; 91 print(o->ch[0]); 92 print(o->ch[1]); 93 rebuild(o, 1, tot); 94 } 95 }else remove(o->ch[d], x); 96 if(o) o->maintain(); 97 } 98 99 int kth(Node *o, int k) { 100 while(o) { 101 int s = szof(o->ch[0]) + 1; 102 if(s == k) return o->v; 103 if(s < k) k -= s, o = o->ch[1]; 104 else o = o->ch[0]; 105 } 106 return -1; 107 } 108 109 int rank(Node *o, int x) { 110 int res = 0; 111 for(int d; o; o = o->ch[d]) { 112 d = o->cmp(x); 113 if(d == 1) res += szof(o->ch[0]) + 1; 114 if(d == -1) d = 0; 115 } 116 return res + 1; 117 } 118 119 int pre(Node *o, int x) { 120 int res = -1; 121 for(int d; o; o = o->ch[d]) { 122 d = o->cmp(x); 123 if(d == 1) res = o->v; 124 if(d == -1) d = 0; 125 } 126 return res; 127 } 128 129 int suf(Node *o, int x) { 130 int res = -1; 131 for(int d; o; o = o->ch[d]) { 132 d = o->cmp(x); 133 if(d == 0) res = o->v; 134 if(d == -1) d = 1; 135 } 136 return res; 137 } 138 139 int main() { 140 #ifdef DEBUG 141 freopen("in.txt", "r", stdin); 142 freopen("out.txt", "w", stdout); 143 #endif 144 145 pis = pool; 146 int n, opt, x; 147 scanf("%d", &n); 148 for(int i = 1; i <= n; i++) { 149 scanf("%d%d", &opt, &x); 150 if(opt == 1) insert(x); 151 else if(opt == 2) remove(root, x); 152 else if(opt == 3) printf("%d\n", rank(root, x)); 153 else if(opt == 4) printf("%d\n", kth(root, x)); 154 else if(opt == 5) printf("%d\n", pre(root, x)); 155 else printf("%d\n", suf(root, x)); 156 } 157 158 return 0; 159 }
打删除标记的还没想好前驱后继怎么求TAT
标签:
原文地址:http://www.cnblogs.com/showson/p/4996035.html