非旋treap學習小記
阿新 • • 發佈:2019-01-10
說在前面
擱了很久的坑,終於遇到題目可以學一發。
聽說這玩意除了LCT,幾乎完爆同是維護平衡樹的splay,好像LCT也可以用treap實現
Treap
顧名思義就是Tree+Heap,即樹堆,既有二叉查詢樹的性質也有堆的性質,通常利用中序遍歷的順序和堆的高度為來解決許多問題,屬於平衡樹的一類。
本質上就是笛卡爾樹,每個點有兩個權值,即為要排序的權值,為隨機權值,用來維護堆的性質,由於權值隨機,這裡建的小根堆高度為,所以在Treap上所有路徑操作和子樹操作時間都是。
非旋Treap
相比一般的旋轉Treap多了非旋兩字,即Treap是靜態的,因此可以支援可持久化操作和其它騷操作。
非旋Treap有兩個核心操作:分裂split和合併merge。
split(x,k):表示將這棵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樹高仍為。同樣是一個遞迴過程。
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上插入點的排名k,把Treap裂成前k的Treap和剩餘的Treap,依次合併Treap,,Treap。刪除類似,對於序列區間操作可以類比。
程式碼十分簡短,常數遠小於均攤複雜度的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));
}
}