您的位置:首页 > 其它

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

2015-11-25 21:47 295 查看
一、斜堆

斜堆是一种可以合并的堆

节点信息:

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(\log{n})$的效果

利用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(\log{n})$的,所以在处理集合信息时不需要额外的并查集,只需要每次暴力往上找到根来比较就能够做到$O(\log{n})$的复杂度了。

举一例带标记的题

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>

using namespace std;

void setIO(const string& a) {
freopen((a+".in").c_str(), "r", stdin);
freopen((a+".out").c_str(), "w", stdout);
}

#define szof(x) ((x) ? (x)->sz : 0)

const int N = 200000 + 10;

struct Node *pis;
struct Node {
int v, sz;
Node *ch[2];

Node(int v = 0) : v(v) {
sz = 1;
ch[0] = ch[1] = 0;
}

void maintain() {
sz = szof(ch[0]) + szof(ch[1]) + 1;
}

int cmp(int x) const {
if(x == v) return -1;
return x < v ? 0 : 1;
}

void *operator new(size_t) {
return pis++;
}
}pool
, *cache
, *root;
int tot = 0;

void print(Node *o) {
if(!o) return;
print(o->ch[0]);
cache[++tot] = o;
print(o->ch[1]);
}

void rebuild(Node*& o, int l, int r) {
if(l > r) return o = 0, void();
int mid = (l + r) >> 1;
o = cache[mid];
rebuild(o->ch[0], l, mid - 1);
rebuild(o->ch[1], mid + 1, r);
o->maintain();
}

void insert(Node*& o, int x) {
if(!o) o = new Node(x);
else {
int d = o->cmp(x);
if(d == -1) d = 0;
insert(o->ch[d], x);
}
o->maintain();
}

void scape(Node*& o, int x) {
int d = o->cmp(x);
if(d == -1) return;
if(o->ch[d]->sz > o->sz * 0.75) {
tot = 0;
print(o);
rebuild(o, 1, tot);
}else scape(o->ch[d], x);
}

void insert(int x) {
insert(root, x);
scape(root, x);
}

void remove(Node*& o, int x) {
int d = o->cmp(x);
if(d == -1) {
if(!o->ch[0] && !o->ch[1]) o = 0;
else {
tot = 0;
print(o->ch[0]);
print(o->ch[1]);
rebuild(o, 1, tot);
}
}else remove(o->ch[d], x);
if(o) o->maintain();
}

int kth(Node *o, int k) {
while(o) {
int s = szof(o->ch[0]) + 1;
if(s == k) return o->v;
if(s < k) k -= s, o = o->ch[1];
else o = o->ch[0];
}
return -1;
}

int rank(Node *o, int x) {
int res = 0;
for(int d; o; o = o->ch[d]) {
d = o->cmp(x);
if(d == 1) res += szof(o->ch[0]) + 1;
if(d == -1) d = 0;
}
return res + 1;
}

int pre(Node *o, int x) {
int res = -1;
for(int d; o; o = o->ch[d]) {
d = o->cmp(x);
if(d == 1) res = o->v;
if(d == -1) d = 0;
}
return res;
}

int suf(Node *o, int x) {
int res = -1;
for(int d; o; o = o->ch[d]) {
d = o->cmp(x);
if(d == 0) res = o->v;
if(d == -1) d = 1;
}
return res;
}

int main() {
#ifdef DEBUG
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif

pis = pool;
int n, opt, x;
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d%d", &opt, &x);
if(opt == 1) insert(x);
else if(opt == 2) remove(root, x);
else if(opt == 3) printf("%d\n", rank(root, x));
else if(opt == 4) printf("%d\n", kth(root, x));
else if(opt == 5) printf("%d\n", pre(root, x));
else printf("%d\n", suf(root, x));
}

return 0;
}


View Code
打删除标记的还没想好前驱后继怎么求TAT
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: