码迷,mamicode.com
首页 > 其他好文 > 详细

斜堆,非旋转treap,替罪羊树

时间:2015-11-25 23:22:07      阅读:334      评论:0      收藏:0      [点我收藏+]

标签:

一、斜堆

斜堆是一种可以合并的堆

节点信息:

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 }
bzoj4003

 

二、不旋转的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 }
View Code

 

三、替罪羊树

说到不用旋转的平衡树,自然还有替罪羊树

之前好像有一种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 }
View Code

打删除标记的还没想好前驱后继怎么求TAT

斜堆,非旋转treap,替罪羊树

标签:

原文地址:http://www.cnblogs.com/showson/p/4996035.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!