非旋treap学习小记

说在前面

搁了很久的坑,终于遇到题目可以学一发。
听说这玩意除了LCT,几乎完爆同是维护平衡树的splay,好像LCT也可以用treap实现

Treap

顾名思义就是Tree+Heap,即树堆,既有二叉查找树的性质也有堆的性质,通常利用中序遍历的顺序和堆的高度为 l o g log log来解决许多问题,属于平衡树的一类。
本质上就是笛卡尔树,每个点有两个权值 v a l , k e y val,key val,key v a l val val即为要排序的权值, k e y key key为随机权值,用来维护堆的性质,由于权值随机,这里建的小根堆高度为 l o g log log,所以在Treap上所有路径操作和子树操作时间都是 O ( l o g ) O(log) O(log)

非旋Treap

相比一般的旋转Treap多了非旋两字,即Treap是静态的,因此可以支持可持久化操作和其它骚操作。
非旋Treap有两个核心操作:分裂split合并merge
split(x,k):表示将 x x x这棵Treap分裂成中序遍历前k的和剩余的两棵Treap,返回这两棵Treap的根。是一个递归操作,分裂时保持了Treap的性质。

pr split(int x,int k){//sz为子树大小,pr为pair<int,int>
	if(!x) return pr(0,0);
	pr y;
	if(sz[tr[x].l]>=k){
		y=split(tr[x].l,k);
		tr[x].l=y.second,update(x);
		y.second=x;
	}
	else{
		y=split(tr[x].r,k-sz[tr[x].l]-1);
		tr[x].r=y.first,update(x);
		y.first=x;
	}
	return y;
}

merge(x,y):基于随机权值的合并,使得操作后的Treap树高仍为 l o g log log。同样是一个递归过程。

int merge(int x,int y){//tr[x].k即为随机权值key
	if(!x || !y) return x^y;
	if(tr[x].k<tr[y].k) return tr[x].r=merge(tr[x].r,y),update(x),x;
	else return tr[y].l=merge(x,tr[y].l),update(y),y;
}

有了split和merge,我们可以实现很多操作,例如插入:找到Treap上插入点 x x x的排名k,把Treap裂成前k的Treap x _x x和剩余的Treap y _y y,依次合并Treap x _x x x x x,Treap y _y y。删除类似,对于序列区间操作可以类比。

代码十分简短,常数远小于均摊复杂度的splay。
例题(模板):BZOJ3224
多了几个操作,注意找排名时是要找最小的即可。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
using namespace std;
const int N=1e5+10,inf=2147483647;
typedef pair<int,int> pr;
struct treap{
	int l,r,v,k;
}tr[N];
int rt;
int sz[N];
void update(int x){
	sz[x]=sz[tr[x].l]+sz[tr[x].r]+1;
}
int tot=0;
int merge(int x,int y){
	if(!x || !y) return x^y;
	if(tr[x].k<tr[y].k) return tr[x].r=merge(tr[x].r,y),update(x),x;
	else return tr[y].l=merge(x,tr[y].l),update(y),y;
}
pr split(int x,int k){
	if(!x) return pr(0,0);
	pr y;
	if(sz[tr[x].l]>=k){
		y=split(tr[x].l,k);
		tr[x].l=y.second,update(x);
		y.second=x;
	}
	else{
		y=split(tr[x].r,k-sz[tr[x].l]-1);
		tr[x].r=y.first,update(x);
		y.first=x;
	}
	return y;
}
int find(int k){
	pr x=split(rt,k-1),y=split(x.second,1);
	int t=y.first;
	rt=merge(merge(x.first,t),y.second);
	return tr[t].v;
}
int rank(int x,int v){
	int tmp=inf,t=0;
    while(x){
        if(v==tr[x].v) tmp=min(tmp,t+sz[tr[x].l]+1);
        if(v>tr[x].v) t+=sz[tr[x].l]+1,x=tr[x].r;
        else x=tr[x].l;
    }
    return tmp==inf?t:tmp;
}
void insert(int v){
	int k=rank(rt,v);
	pr x=split(rt,k);
	sz[++tot]=1,tr[tot].v=v,tr[tot].k=rand();
	rt=merge(merge(x.first,tot),x.second);
}
void del(int v){
	int k=rank(rt,v);
	pr x=split(rt,k-1),y=split(x.second,1);
	rt=merge(x.first,y.second);
}
int next(int v){
	int x=rt,tmp=inf;
	while(x) tr[x].v>v?(tmp=min(tmp,tr[x].v),x=tr[x].l):x=tr[x].r;
	return tmp;
}
int last(int v){
	int x=rt,tmp=-inf;
	while(x) tr[x].v<v?(tmp=max(tmp,tr[x].v),x=tr[x].r):x=tr[x].l;
	return tmp;
}
int main()
{
	int n;
	scanf("%d",&n);
	while(n--){
		int op,x;
		scanf("%d %d",&op,&x);
		if(op==1) insert(x);
		else if(op==2) del(x);
		else if(op==3) printf("%d\n",rank(rt,x));
		else if(op==4) printf("%d\n",find(x));
		else if(op==5) printf("%d\n",last(x));
		else printf("%d\n",next(x));
	}
}

posted @ 2018-10-24 16:55  sadstone  阅读(48)  评论(0编辑  收藏  举报