無旋Treap 從狂轉到不轉
在學習平衡樹部分時,旁邊的某位C姓dalao對Treap情有獨鍾,而我卻為Splay的優美而深深著迷.這導致了對Treap的不屑一顧
這東西有什麼用,那麼多操作都不資瓷,low
如今繁華落盡,每次遇到需要使用平衡樹的題時,不禁流下了悔恨的淚(汗)水
Splay真xx**難打!
所以,今天,就讓我們來講講無旋Treap
無旋Treap
無旋Treap最大的優勢就是編碼簡單,支援的操作多,相應的,它比Splay要稍稍慢一點
無旋Treap的主要操作有兩個,split
和merge
,能分裂合併兩棵Treap.通過這兩個操作,就可以拓展出大量的其他操作
分裂
Treap的split
支援在某棵Treap中分裂出大小為的左子樹,或者是分裂出權值小於的子樹
由於Treap具有二叉搜尋樹的性質,按size
key
遞迴分裂即可
同時通過物件維護分裂後兩顆treap各自的位置關係
按size
分裂
void split(int now,int num,int &x,int &y) {
if(!now) x=y=0;
else {
pushdown(now);
if(num<=siz[L]) {y=now;split(L,num,x,L);}
else {x=now;split(R,num-siz[L]-1,R,y);}
update(now);
}
}
按key
分裂
void split(int now,int k,int &x,int &y) {
if(!now) x=y=0;
else {
//小於k的在左子樹,大於等於key的在右子樹
if(k<key[now]) {y=now;split(L,k,x,L);}
else {x=now;split(R,k,R,y);}
update(now);
}
}
合併
Treap的合併操作將兩棵Treap以一定的次序組合起來
其最終形態由兩方面決定:相對位置
和pos
(我通常將其叫做自平衡權)
顯然,根據Treap的原理,通常pos
較小的放在靠上的位置,同時treap又是二叉搜尋樹,那麼其相對位置決定了兩者之間的父子關係(屬於左子樹右子樹)
類似與左偏樹一樣,遞迴合併即可
int merge(int a,int b) {
if(a*b==0) return a+b;
if(pos[a]<pos[b]) {
ch[a][1]=merge(ch[a][1],b);
update(a);
return a;
}
else {
ch[b][0]=merge(a,ch[b][0]);
update(b);
return b;
}
}
插入
通過split
和merge
,我們可以簡單的實現insert
按位置插入
首先將要插入的位置左側部分分裂出來,然後依次合併左側部分,插入節點,右側部分即可
按權值插入
同理,將小於的節點放在左子樹,依次合併
void insert(int v) {
int a,b;
split(rt,v,a,b);
rt=merge(merge(a,make_node(v)),b);
}
刪除
通過split
和merge
,我們可以簡單的實現del
按位置刪除
把刪除位置提取出來,合併左右節點
按權值刪除
把小於等於k的節點提取出來,再小於k-1的的部分提取出來,由於可能有多等於的值,直接將等於的子樹合併左右兒子節點,這樣就保證了僅僅刪除一個節點.若要全部刪除,直接合並左右部分
void del(int v) {
int a,b,c;
split(rt,v,a,b);
split(a,v-1,a,c);
c=merge(ch[c][0],ch[c][1]);
rt=merge(merge(a,c),b);
}
區間操作
類似於splay
,先利用split
提取區間,然後打上標記,最後merge
比如說,區間翻轉
void rev(int l,int r) {
split(rt,r+1,a,b);
split(a,l,c,d);
rev[d]^=1;
swap(ch[d][0],ch[d][1]);
}
實踐
BZOJ 3223 文藝平衡樹
#include <bits/stdc++.h>
using namespace std;
#define L ch[now][0]
#define R ch[now][1]
#define mid ((l+r)>>1)
typedef pair <int,int> p;
const int N = 100010;
int n,m,rt;
struct treap {
int pos[N],ch[N][2],siz[N],rev[N],rt,key[N];
void update(int now) {siz[now]=siz[L]+siz[R]+1;}
void Rev(int now) {swap(L,R);}
void pushdown(int now) {if(rev[now]) {rev[now]^=1;rev[L]^=1;rev[R]^=1;Rev(L);Rev(R);}}
int merge(int a,int b) {
if(a*b==0) return a?a:b;
pushdown(a);pushdown(b);
if(pos[a]<pos[b]) {
ch[a][1]=merge(ch[a][1],b);
update(a);
return a;
}
else {
ch[b][0]=merge(a,ch[b][0]);
update(b);
return b;
}
}
void split(int now,int num,int &x,int &y) {
if(!now) x=y=0;
else {
pushdown(now);
if(num<=siz[L]) {y=now;split(L,num,x,L);}
else {x=now;split(R,num-siz[L]-1,R,y);}
update(now);
}
}
int build(int l,int r) {
if(l==r) {pos[l]=rand();siz[l]=1;key[l]=l-1;return l;}
return merge(build(l,mid),build(mid+1,r));
}
void print(int now) {
pushdown(now);
if(L) print(L);
if(key[now]>=1 && key[now]<=n) printf("%d ",key[now]);
if(R) print(R);
}
}t;
int read() {
int _ans=0,_flag=1;
char _ch=getchar();
while((_ch != '-') && (_ch > '9' || _ch < '0')) _ch=getchar();
if(_ch == '-') {_flag = -1;_ch = getchar();}
while(_ch >= '0' && _ch <= '9') {_ans=_ans*10+_ch-'0';_ch=getchar();}
return _ans*_flag;
}
int main() {
n=read();m=read();
rt=t.build(1,n+2);
for(int i=1;i<=m;++i) {
int l=read(),r=read(),a,b,c,d;
t.split(rt,r+1,a,b);
t.split(a,l,c,d);
t.rev[d]^=1;
t.Rev(d);
rt=t.merge(c,t.merge(d,b));
}
t.print(rt);
return 0;
}
LG-2464 [SDOI2008]鬱悶的小J
#include <bits/stdc++.h>
using namespace std;
#define L ch[now][0]
#define R ch[now][1]
const int N = 400010;
map <int,int> p;
struct Treap {
int key[N],pos[N],cnt,ch[N][2],rt[N],siz[N];
int newnode(int k) {pos[++cnt]=rand();siz[cnt]=1;key[cnt]=k;return cnt;}
void update(int now) {siz[now]=1+siz[L]+siz[R];}
void split(int now,int k,int &x,int &y) {
if(!now) {x=y=0;return;}
if(k<key[now]) {y=now;split(L,k,x,L);}
else {x=now;split(R,k,R,y);}
update(now);
}
int merge(int a,int b) {
if(a*b==0) return a+b;
if(pos[a]<pos[b]) {
ch[a][1]=merge(ch[a][1],b);
update(a);
return a;
}
else {
ch[b][0]=merge(a,ch[b][0]);
update(b);
return b;
}
}
void insert(int k,int v) {
int a,b;
split(rt[k],v,a,b);
rt[k]=merge(a,merge(newnode(v),b));
}
void del(int k,int v) {
int a,b,c;
split(rt[k],v,a,b);
split(a,v-1,a,c);
c=merge(ch[c][0],ch[c][1]);
rt[k]=merge(merge(a,c),b);
}
void query(int k,int l,int r) {
int a,b,c;
split(rt[k],r,a,b);
split(a,l-1,a,c);
printf("%d\n",siz[c]);
rt[k]=merge(merge(a,c),b);
}
}t;
int n,m,cnt,a[N];
int read();
int main() {
srand(time(NULL));
n=read();m=read();
for(int i=1;i<=n;++i) {
a[i]=read();
if(p[a[i]]==0) p[a[i]]=++cnt;
a[i]=p[a[i]];
t.insert(a[i],i);
}
for(int i=1;i<=m;++i) {
char ch=getchar();
while(ch!='Q' && ch!='C') ch=getchar();
if(ch=='Q') {
int l=read(),r=read(),k=read();
k=p[k];
t.query(k,l,r);
}
else {
int pos=read(),k=read();
t.del(a[pos],pos);
if(p[k]==0) p[k]=++cnt;
a[pos]=p[k];
t.insert(a[pos],pos);
}
}
return 0;
}
int read() {
int _ans=0,_flag=1;
char _ch=getchar();
while((_ch != '-') && (_ch > '9' || _ch < '0')) _ch=getchar();
if(_ch == '-') {_flag = -1;_ch = getchar();}
while(_ch >= '0' && _ch <= '9') {_ans=_ans*10+_ch-'0';_ch=getchar();}
return _ans*_flag;
}