1. 程式人生 > >非旋treap學習小記

非旋treap學習小記

說在前面

擱了很久的坑,終於遇到題目可以學一發。
聽說這玩意除了LCT,幾乎完爆同是維護平衡樹的splay,好像LCT也可以用treap實現

Treap

顧名思義就是Tree+Heap,即樹堆,既有二叉查詢樹的性質也有堆的性質,通常利用中序遍歷的順序和堆的高度為loglog來解決許多問題,屬於平衡樹的一類。
本質上就是笛卡爾樹,每個點有兩個權值val,keyval,keyvalval即為要排序的權值,keykey為隨機權值,用來維護堆的性質,由於權值隨機,這裡建的小根堆高度為loglog,所以在Treap上所有路徑操作和子樹操作時間都是O(log)O(log)

非旋Treap

相比一般的旋轉Treap多了非旋兩字,即Treap是靜態的,因此可以支援可持久化操作和其它騷操作。
非旋Treap有兩個核心操作:分裂split合併merge
split(x,k):表示將xx這棵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樹高仍為loglog。同樣是一個遞迴過程。

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上插入點xx的排名k,把Treap裂成前k的Treapx

_x和剩餘的Treapy_y,依次合併Treapx_xxx,Treapy_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));
	}
}