Treap与fhq_Treap模板(支持内存回收)

1.普通Treap

通过左右旋来维护堆的性质

左右旋是不改变中序遍历的

这里有几点要注意一下:

  (1).由于写了内存回收,在 newnode 的时候要 memset 一下这个节点,因为可能被用过

  (2).在删除时要这么写 : 如果只有小于等于1个儿子就直接删

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cstdio>
#include<cmath>
#include<ctime>
using namespace std;
 
const int MAXN = 100100, inf = 0x7fffffff;
 
struct Node{
    int ch[2];
    int val, prio;
    int cnt, siz;
//  Node(){ch[0] = ch[1] = val = cnt = siz = 0;}
}t[MAXN];
 
 
int n;
int root, pool_cur, delpool[MAXN], delcur;
 
void init();
int newnode(int val);
void delnode(int cur);
void pushup(int cur);
void rotate(int &cur, int d);
void Insert(int &cur, int val);
void Remove(int &cur, int val);
int getpre(int val);
int getnxt(int val);
int getrankbyval(int cur, int val);
int getvalbyrank(int cur, int rank);
 
int main() {
    srand(time(NULL));
    scanf("%d", &n);
    int opt, x;
    init();
    for(int i = 1; i <= n; ++i) {
        scanf("%d%d", &opt, &x);
        switch(opt) {
            case 1:{
                Insert(root, x);
                break;
            }
            case 2:{
                Remove(root, x);
                break;
            }
            case 3:{
                printf("%d\n", getrankbyval(root, x) - 1);
                break;
            }
            case 4:{
                printf("%d\n", getvalbyrank(root, x + 1));
                break;
            }
            case 5:{
                printf("%d\n", getpre(x));
                break;
            }
            case 6:{
                printf("%d\n", getnxt(x));
                break;
            }
        }
    }
    return 0;
}
 
void init() {
    newnode(-inf);
    newnode(inf);
    root = 1;
    t[1].ch[1] = 2;
    pushup(root);
    return;
}

inline void delnode(int cur) {
    delpool[++delcur] = cur;
    return;
}
 
int newnode(int val) {
    int cur = (delcur ? delpool[delcur--] : ++pool_cur);
    memset(t + cur, 0, sizeof(Node));
    t[cur].siz = t[cur].cnt = 1;
    t[cur].prio = rand();
    t[cur].val = val;
    return cur;
}
 
void pushup(int cur) {
    t[cur].siz = t[t[cur].ch[0]].siz + t[t[cur].ch[1]].siz + t[cur].cnt;
    return;
}
 
void rotate(int &cur, int d) {
    int u = t[cur].ch[d];
    t[cur].ch[d] = t[u].ch[d^1];
    t[u].ch[d^1] = cur;
    t[u].siz = t[cur].siz;
    pushup(cur);
    cur = u;
    return;
}
 
void Insert(int &cur, int val) {
    if(cur == 0) {
        cur = newnode(val);
        return;
    }
    if(t[cur].val == val) {
        ++t[cur].cnt;
        pushup(cur);
        return;
    }
    int d = t[cur].val < val;
    Insert(t[cur].ch[d], val);
    pushup(cur);
    if(t[t[cur].ch[d]].prio < t[cur].prio) rotate(cur, d);
    return;
}
 
void Remove(int &cur, int val) {
    if(!cur) return;
    if(t[cur].val == val) {
        int o = cur;
        if(t[cur].cnt > 1) {
            --t[cur].cnt;
        } else {
            if(!t[cur].ch[0]) {
                cur = t[cur].ch[1];
                delnode(o);
            } else if(!t[cur].ch[1]) {
                cur = t[cur].ch[0];
            	delnode(o);
            } else {
                int d = t[t[cur].ch[0]].prio < t[t[cur].ch[1]].prio;
                rotate(cur, d ^ 1);
                Remove(t[cur].ch[d], val);
            }
        }
        pushup(cur);
    } else {
        int d = t[cur].val < val;
        Remove(t[cur].ch[d], val);
    }
    pushup(cur);
    return;
}
 
int getpre(int val) {
    int ans = 1;
    int cur = root;
    while(cur) {
        if(val == t[cur].val) {
            if(t[cur].ch[0] > 0) {
                cur = t[cur].ch[0];
                while(t[cur].ch[1] > 0) cur = t[cur].ch[1];
                ans = cur;
            }
            break;
        }
        if(t[cur].val < val and t[cur].val > t[ans].val) ans = cur;
        cur = t[cur].val > val ? t[cur].ch[0] : t[cur].ch[1];
    }
    return t[ans].val;
}
 
int getnxt(int val) {
    int ans = 2;
    int cur = root;
    while(cur) {
        if(val == t[cur].val) {
            if(t[cur].ch[1] > 0) {
                cur = t[cur].ch[1];
                while(t[cur].ch[0] > 0) cur = t[cur].ch[0];
                ans = cur;
            }
            break;
        }
        if(t[cur].val > val and t[cur].val < t[ans].val) ans = cur;
        cur = t[cur].val > val ? t[cur].ch[0] : t[cur].ch[1];
    }
    return t[ans].val;
}
 
int getrankbyval(int cur, int val) {
    if(!cur) return 0;
    if(val == t[cur].val) return t[t[cur].ch[0]].siz + 1;
    return t[cur].val > val ? getrankbyval(t[cur].ch[0], val) : (getrankbyval(t[cur].ch[1], val) + t[t[cur].ch[0]].siz + t[cur].cnt);
}
 
int getvalbyrank(int cur, int rank) {
    if(!cur) return 0;
    if(t[t[cur].ch[0]].siz >= rank) return getvalbyrank(t[cur].ch[0], rank);
    if(t[t[cur].ch[0]].siz + t[cur].cnt >= rank) return t[cur].val;
    return getvalbyrank(t[cur].ch[1], rank - t[t[cur].ch[0]].siz - t[cur].cnt);
}

 

2.fhq_Treap (非旋 Treap )

通过 Split 和 Merge 操作来维护平衡树

(1) 用 fhq_Treap 实现普通 Treap 支持的操作

  Split :

    把以 cur 为根的树的前 k 个元素分离开

    返回值为分离后的两根

pair<int, int> Split(int cur, int k) {
	if(!cur or !k) return make_pair(0, cur);
	pair<int, int> res;
	if(t[lson].siz >= k) {
		res = Split(lson, k);
		lson = res.second;
		pushup(cur);
		res.second = cur;
	} else {
		res = Split(rson, k - t[lson].siz - 1);
		rson = res.first;
		pushup(cur);
		res.first = cur;
	}
	return res;
}

    进入右子树,则一定选当前节点,将它作为分离后左边那棵树的根

    进入左子树,则一定不选当前节点,将它作为分离后的右边那棵树的根

  Merge : 

    像可并堆那样进行合并

    这样来满足堆性质

int Merge(int x, int y) {
	if(!x) return y; if(!y) return x;
	if(t[x].prio < t[y].prio) {
		t[x].ch[1] = Merge(t[x].ch[1], y);
		pushup(x);
		return x;
	} else {
		t[y].ch[0] = Merge(x, t[y].ch[0]);
		pushup(y);
		return y;
	}
}

这里写一种比较清奇的 getrank , 返回所有小于 val 的元素个数

很好用的

int getrnk(int cur, int val)  {
	if(!cur) return 0;
	return val <= t[cur].val ? getrnk(lson, val) : (getrnk(rson, val) + t[lson].siz + 1);
}

加哨兵总是挂,最后就没加 = =

好像也不用加

 

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cstdio>
#include<cmath>
#include<ctime>
#define lson t[cur].ch[0]
#define rson t[cur].ch[1]
using namespace std;
 
const int MAXN = 100001;
 
struct Node{
    int ch[2], siz, prio, val;
}t[MAXN];
int n, Root, poolcur;
int delpool[MAXN], delcur;
 
inline void pushup(int cur) {
    t[cur].siz = t[lson].siz + t[rson].siz + 1;
    return;
}
inline void delnode(int cur) {
	delpool[++delcur] = cur;
	return;
}
inline int newnode(int val) {
    register int cur = (delcur ? delpool[delcur--] : ++poolcur);
    memset(t + cur, 0, sizeof(Node));
    t[cur].val = val;
    t[cur].siz = 1;
    t[cur].prio = rand();
    return cur;
}
pair<int, int> Split(int cur, int k) {
    if(!cur or !k) return make_pair(0, cur);
    pair<int, int> res;
    if(t[lson].siz >= k) {
        res = Split(lson, k);
        lson = res.second;
        pushup(cur);
        res.second = cur;
    } else {
        res = Split(rson, k - t[lson].siz - 1);
        rson = res.first;
        pushup(cur);
        res.first = cur;
    }
    return res;
}
int Merge(int x, int y) {
    if(!x) return y; if(!y) return x;
    if(t[x].prio < t[y].prio) {
        t[x].ch[1] = Merge(t[x].ch[1], y);
        pushup(x);
        return x;
    } else {
        t[y].ch[0] = Merge(x, t[y].ch[0]);
        pushup(y);
        return y;
    }
}
int getrnk(int cur, int val)  {
    if(!cur) return 0;
    return val <= t[cur].val ? getrnk(lson, val) : (getrnk(rson, val) + t[lson].siz + 1);
}
int findkth(int k) {
    pair<int, int> x = Split(Root, k - 1);
    pair<int, int> y = Split(x.second, 1);
    int ans = t[y.first].val;
    Root = Merge(Merge(x.first, y.first), y.second);
    return ans;
}
int getpre(int val) {
    register int k = getrnk(Root, val);
    return findkth(k);
}
int getnxt(int val) {
    register int k = getrnk(Root, val + 1);
    return findkth(k + 1);
}
void Insert(int val) {
    pair<int, int> x = Split(Root, getrnk(Root, val));
    Root = Merge(Merge(x.first, newnode(val)), x.second);
    return;
}
void Remove(int val) {
    register int k = getrnk(Root, val);
    pair<int, int> x = Split(Root, k);
    pair<int, int> y = Split(x.second, 1);
    delnode(y.first);
    Root = Merge(x.first, y.second);
    return;
}
inline int rd() {
    register int x = 0;
    register char c = getchar();
    register bool f = false;
    while(!isdigit(c)) {
        if(c == '-') f = true;
        c = getchar();
    }
    while(isdigit(c)) {
        x = x * 10 + c - 48;
        c = getchar();
    }
    return f ? -x : x;
}
 
int main() {
    srand(time(NULL));
    memset(t, 0, sizeof(Node)); 
    n = rd();
    int opt, x;
    while(n--) {
        opt = rd(); x = rd();
        switch(opt) {
            case 1: {
                Insert(x);
                break;
            }
            case 2: {
                Remove(x);
                break;
            }
            case 3: {
                printf("%d\n", getrnk(Root, x) + 1);
                break;
            }
            case 4: {
                printf("%d\n", findkth(x));
                break;
            }
            case 5: {
                printf("%d\n", getpre(x));
                break;
            }
            case 6: {
                printf("%d\n", getnxt(x));
                break;
            }
        }
    }
    return 0;
}

 


 

(2) 用 fhq_Treap 实现 Splay 支持的区间操作

  fhq_Treap是支持拼接子树的,所以也能够很好的支持区间操作

  要对区间 [ l, r ] 进行操作,就先把前 r 个元素 Split 出来,再将前 l - 1 个元素 Split 出来,这样就得到了区间 [ l, r ] 的一棵Treap

  之后进行想要的操作即可

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cstdio>
#include<cmath>
#include<ctime>
#define lson t[cur].ch[0]
#define rson t[cur].ch[1]
using namespace std;

const int MAXN = 100001;

struct Node{
	int ch[2], siz, val, prio;
	bool rvrs;
	Node(){ch[0] = ch[1] = siz = val = 0; rvrs = false;}
}t[MAXN];
int n, m, Root, poolcur;

inline int rd() {
	register int x = 0;
	register char c = getchar();
	register bool f = false;
	while(!isdigit(c)) {
		if(c == '-') f = true;
		c = getchar();
	}
	while(isdigit(c)) {
		x = x * 10 + c - 48;
		c = getchar();
	}
	return f ? -x : x;
}
inline void pushup(int cur) {
	t[cur].siz = t[lson].siz + t[rson].siz + 1;
	return;
}
inline void pushdown(int cur) {
	if(cur && t[cur].rvrs) {
		t[cur].rvrs = false;
		swap(lson, rson);
		t[lson].rvrs ^= 1;
		t[rson].rvrs ^= 1;
	}
	return;
}
inline int newnode(int val) {
	register int cur = ++poolcur;
	t[cur].val = val;
	t[cur].siz = 1;
	t[cur].prio = rand();
	t[cur].rvrs = false;
	return cur;
}
pair<int, int> Split(int cur, int k) {
	if(!cur or !k) return make_pair(0, cur);
	pushdown(cur); pair<int, int> res;
	if(t[lson].siz >= k) {
		res = Split(lson, k);
		lson = res.second;
		pushup(cur);
		res.second = cur;
	} else {
		res = Split(rson, k - t[lson].siz - 1);
		rson = res.first;
		pushup(cur);
		res.first = cur;
	}
	return res;
}
int Merge(int x, int y) {
	pushdown(x); pushdown(y);
	if(!x) return y; if(!y) return x;
	if(t[x].prio < t[y].prio) {
		t[x].ch[1] = Merge(t[x].ch[1], y);
		pushup(x);
		return x;
	} else {
		t[y].ch[0] = Merge(x, t[y].ch[0]);
		pushup(y);
		return y;
	}
}
int getrnk(int cur, int val) {
	if(!cur) return 0;
	return val <= t[cur].val ? getrnk(lson, val) : (getrnk(rson, val) + t[lson].siz + 1);
}
void Insert(int val) {
	pair<int, int> x = Split(Root, getrnk(Root, val));
	Root = Merge(Merge(x.first, newnode(val)), x.second);
	return;
}
void Reverse(int l, int r) {
	pair<int, int> x = Split(Root, r);
	pair<int, int> y = Split(x.first, l - 1);
	t[y.second].rvrs ^= 1;
	Root = Merge(Merge(y.first, y.second), x.second);
	return;
}
void Recycle(int cur) {
	if(!cur) return;
	pushdown(cur);
	if(lson) Recycle(lson);
	if(t[cur].val > 0 and t[cur].val <= n) printf("%d ", t[cur].val);
	if(rson) Recycle(rson);
	return;
}
inline void init() {
	t[1].val = t[1].siz = 1;
	Root = 1; poolcur = 1;
	t[1].prio = rand();
	return;
}

int main() {
	srand(time(NULL));
	n = rd(); m = rd();
	init();
	for(int i = 2; i <= n; ++i) Insert(i);
	int l, r;
	while(m--) {
		l = rd(); r = rd();
		Reverse(l, r);
	}
	Recycle(Root);
	putchar('\n');
	return 0;
}

Update: 

  在写 bzoj1500 时有些奇怪的疑惑: Split 出来之后再 Merge 回去还是原来的树吗?区间反转的 tag 给当前根打上之后再接回去不会 GG 吗?

  显然,它还是原来的树,reverse 之后也并不会 GG . 当原来的 x.first 回到它应该的位置后,x.second 整棵子树靠近 x.first 一侧的 tag 也已经下放至更深一层了


 

posted @ 2018-05-20 21:51  EvalonXing  阅读(330)  评论(0编辑  收藏  举报