無旋Treap——從入門到放棄
前言
已經是從入門到放棄的第四篇了。
但是本文並不打算給大家講無旋Treap複雜度證明一類的。
很顯然每次操作都是期望
什麼是Treap?
Treap=Tree+heap
其核心思想在於在權值上維護一棵二叉查詢樹,在優先順序上維護一個堆
有旋treap利用旋轉操作來維護堆的性質,
而無旋treap利用有序構樹維護堆的性質。
無旋Treap的兩大構樹順序:權值與原排列
權值構樹是很常見的一種構樹方法。和有旋treap一樣,左兒子與右兒子分別表示比自己小或比自己大的樹,同時其優先順序均低於該節點。這類問題用有旋treap也能夠很好地解決。這樣的題有很多,比如bzoj3224普通平衡樹
但很不幸的是,很多與平衡樹沾邊的題目大多有維護一個原有有序序列的要求,這個要求基本上就把有旋treap幹掉了。但是對於無旋treap,我們可以按原有的序列進行構樹,這樣就可以維護一個原有的有序排列了。
無旋treap的核心操作:split與merge
但是在構樹之後肯定是有修改操作的。這點是毋庸置疑的。對於有旋treap,我們可以通過旋轉來插入要加入的權值或是刪除對應的節點,但對於可能需要進行區間操作的無旋treap,我們顯然不能直接這樣做。
此時,因為無旋treap的有序性,我們可以像一般的有旋treap一樣對需要
插入/刪除的部分進行定位查詢。
如果是對於權值有序,像普通的有旋treap一樣直接遞迴查詢即可,
如果是對於原序列有序,則維護一組指標,也可以很方便地進行查詢。
那麼在查詢到了相應的樹中位置之後,我們需要做的就是——
把這棵樹拆開
split
整個無旋treap的核心就是它的有序性。
在找到了需要操作的位置後,我們可以把這棵樹拆成兩棵有序的樹。
對於查詢到的節點,我們根據該節點左兒子的大小對這個節點應該在哪棵樹進行判斷,然後遞迴處理即可。
inline D split(Treap* pos,int k){
if(pos==NULL) return D(NULL,NULL);
D y;
if(sze(pos->son[0])>=k){
y=split(pos->son[0],k);
pos->son[0]=y.second;
pos->update();
y.second=pos;
}else{
y=split(pos->son[1],k-1-sze(pos->son[0]));
pos ->son[1]=y.first;
pos->update();
y.first=pos;
}
return y;
}
merge
在你進行了加入/刪除操作之後,你發現你手上現在有兩到三棵樹了,自然我們需要將這些樹合併。合併的具體操作也是遞迴處理,此時就可以維護無旋treap的堆性質。
但是需要注意的是,合併時兩棵樹的左右位置,維護的是整棵樹的有序性。
inline Treap* merge(Treap* a,Treap* b){
if(!a) return b;
if(!b) return a;
if(a->weight<b->weight){
a->son[1]=merge(a->son[1],b);
a->update();
return a;
}else{
b->son[0]=merge(a,b->son[0]);
b->update();
return b;
}
}
水得如壺口瀑布一樣的水題
(1)bzoj3224:普通平衡樹
題面見連結 傳送門
最簡單最基礎的權值排序外加單點修改查詢
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;
inline int read(){
int i=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch);ch=getchar())
if(ch=='-') f=-1;
for(;isdigit(ch);ch=getchar())
i=(i<<3)+(i<<1)+(ch^48);
return i*f;
}
int buf[1024];
inline void write(int x){
if(!x){putchar('0');return ;}
if(x<0){putchar('-');x=-x;}
while(x){buf[++buf[0]]=x%10,x/=10;}
while(buf[0]) putchar(buf[buf[0]--]+48);
return ;
}
struct Treap{
Treap* son[2];
int weight,sze,data;
Treap(int v){
sze=1,data=v;weight=rand();
son[1]=son[0]=NULL;
return ;
}
inline void update(){
sze=1+(son[0]!=NULL?son[0]->sze:0)+(son[1]!=NULL?son[1]->sze:0);
return ;
}
}*root;
typedef pair<Treap*,Treap*>D;
inline int sze(Treap* pos){
return pos?pos->sze:0;
}
int n,ord,x;
inline Treap* merge(Treap* a,Treap* b){
if(!a) return b;
if(!b) return a;
if(a->weight<b->weight){
a->son[1]=merge(a->son[1],b);
a->update();
return a;
}else{
b->son[0]=merge(a,b->son[0]);
b->update();
return b;
}
}
inline D split(Treap* pos,int k){
if(pos==NULL) return D(NULL,NULL);
D y;
if(sze(pos->son[0])>=k){
y=split(pos->son[0],k);
pos->son[0]=y.second;
pos->update();
y.second=pos;
}else{
y=split(pos->son[1],k-1-sze(pos->son[0]));
pos->son[1]=y.first;
pos->update();
y.first=pos;
}
return y;
}
inline int getrank(Treap* pos,int x){
if(pos==NULL) return 0;
else return (pos->data>=x)?getrank(pos->son[0],x):getrank(pos->son[1],x)+1+sze(pos->son[0]);
}
inline int getkth(int k){
D x=split(root,k-1);
D y=split(x.second,1);
Treap* pos=y.first;
root=merge(x.first,merge(pos,y.second));
return pos==NULL?0:pos->data;
}
inline void insert(int d){
int k=getrank(root,d);
D x=split(root,k);
Treap* pos=new Treap(d);
root=merge(x.first,merge(pos,x.second));
return ;
}
inline void remove(int d){
int k=getrank(root,d);
D x=split(root,k);
D y=split(x.second,1);
root=merge(x.first,y.second);
return ;
}
signed main(){
n=read();
for(int i=1;i<=n;++i){
ord=read();x=read();
switch(ord){
case 1:insert(x);break;
case 2:remove(x);break;
case 3:write(getrank(root,x)+1);puts("");break;
case 4:write(getkth(x));puts("");break;
case 5:write(getkth(getrank(root,x)));puts("");break;
case 6:write(getkth(getrank(root,x+1)+1));puts("");break;
}
}
return 0;
}
(2)bzoj1503 鬱悶的出納員
題面依然見連結點這裡
很明顯你不能直接進行全域性修改對伐。這個時候你需要打一個差分。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;
inline int read(){
int i=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch);ch=getchar())
if(ch=='-') f=-1;
for(;isdigit(ch);ch=getchar())
i=(i<<3)+(i<<1)+(ch^48);
return i*f;
}
int buf[1024];
inline void write(int x){
if(!x){putchar('0');return ;}
if(x<0){putchar('-');x=-x;}
while(x){buf[++buf[0]]=x%10,x/=10;}
while(buf[0]) putchar(buf[buf[0]--]+48);
return ;
}
#define stan 11
struct Treap{
Treap* son[2];
int sze,val,weight;
Treap(){val=sze=0,weight=rand();son[1]=son[0]=NULL;}
inline void update(){
sze=1+son[0]->sze+son[1]->sze;
}
}*null=new Treap,*root=null;
typedef pair<Treap*,Treap*> D;
int m,mini,tmp=0,x,cnt;
char ord[stan];
inline Treap* newtreap(int x){
Treap* pos=new Treap();
pos->son[0]=pos->son[1]=null;
pos->sze=1;pos->val=x;
return pos;
}
inline Treap* merge(Treap* a,Treap* b){
if(a==null) return b;
if(b==null) return a;
if(a->weight<b->weight){
a->son[1]=merge(a->son[1],b);
a->update();
return a;
}else{
b->son[0]=merge(a,b->son[0]);
b->update();
return b;
}
}
inline D split(Treap* pos,int k){
if(pos==null) return D(null,null);
D y;
if(pos->son[0]->sze>=k){
y=split(pos->son[0],k);
pos->son[0]=y.second;
pos->update();
y.second=pos;
}else{
y=split(pos->son[1],k-1-pos->son[0]->sze);
pos->son[1]=y.first;
pos->update();
y.first=pos;
}
return y;
}
inline int getrank(Treap* pos,int x){
if(pos==null) return 0;
return (pos->val>=x)?getrank(pos->son[0],x):getrank(pos->son[1],x)+1+pos->son[0]->sze;
}
inline int getkth(int k){
D x=split(root,k-1);
D y=split(x.second,1);
Treap* pos=y.first;
root=merge(x.first,merge(pos,y.second));
return pos->val;
}
inline void insert(int d){
int k=getrank(root,d);
D x=split(root,k);
Treap* pos=newtreap(d);
root=merge(x.first,merge(pos,x.second));
return ;
}
inline void remove(){
D x=split(root,1);
root=x.second;
++cnt;
return ;
}
signed main(){
m=read();mini=read();
while(m--){
scanf("%s",ord);x=read();
switch(ord[0]){
case 'I':if(x>=mini) insert(x-tmp);break;
case 'F':{
if(root==null||root->sze<x)
puts("-1");
else{
write(getkth(root->sze-x+1)+tmp);
puts("");
}
break;
}
case 'A':tmp+=x;break;
case 'S':{
tmp-=x;
while(root!=null&&getkth(1)+tmp<mini)
remove();
break;
}
}
}
write(cnt);
return 0;
}
(3)bzoj3223文藝平衡樹
題面照例見連結點我點我
基礎的區間翻轉操作,像線段樹一樣打標記即可
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;
inline int read(){
int i=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch);ch=getchar())
if(ch=='-') f=-1;
for(;isdigit(ch);ch=getchar())
i=(i<<3)+(i<<1)+(ch^48);
return i*f;
}
int buf[1024];
inline void write(int x){
if(!x){putchar('0');return ;}
if(x<0){putchar('-');x=-x;}
while(x){buf[++buf[0]]=x%10,x/=10;}
while(buf[0]) putchar(buf[buf[0]--]+48);
return ;
}
#define stan 555555
struct Treap{
Treap* son[2];
int val,weight,sze;bool flip;
Treap(){
val=-999999999;sze=0;weight=rand();flip=false;
return ;
}
inline void update(){
sze=son[1]->sze+son[0]->sze+1;
return ;
}
}*null=new Treap(),*root=null,*stack[stan],*x,*last;
typedef pair<Treap*,Treap*> D;
int n,m,sta,en;
inline void maintain_flip(Treap* pos){
if(pos==null) return ;
pos->flip^=1;
return ;
}
inline void pushdown(Treap* pos){
if(pos==null) return ;
if(pos->flip){
pos->flip^=1;
maintain_flip(pos->son[0]);
maintain_flip(pos->son[1]);
swap(pos->son[0],pos->son[1]);
}
return ;
}
inline Treap* newtreap(int val){
Treap *pos=new Treap();
pos->son[1]=pos->son[0]=null;pos->weight=rand();
pos->val=val;pos->sze=1;pos->flip=0;
return pos;
}
inline Treap* merge(Treap* a,Treap* b){
if(a==null) return b;
if(b==null) return a;
pushdown(a);pushdown(b);
if(a->weight<b->weight){
a->son[1]=merge(a->son[1],b);
a->update();
return a;
}else{
b->son[0]=merge(a,b->son[0]);
b->update();
return b;
}
}
inline D split(Treap* pos,int k){
if(pos==null) return D(null,null);
D y;pushdown(pos);
if(pos->son[0]->sze>=k){
y=split(pos->son[0],k);
pos->son[0]=y.second;
pos->update();
y.second=pos;
}else{
y=split(pos->son[1],k-1-pos->son[0]->sze);
pos->son[1]=y.first;
pos->update();
y.first=pos;
}
return y;
}
inline Treap* build(){
int p=0;
for(int i=1;i<=n;++i){
x=newtreap(i);last=null;
while(p&&stack[p]->weight>x->weight){
stack[p]->update();
last=stack[p];
stack[p--]=null;
}
if(p) stack[p]->son[1]=x;
x->son[0]=last;stack[++p]=x;
}
while(p) stack[p--]->update();
return stack[1];
}
inline void reverse(){
sta=read();en=read();
D x=split(root,sta-1);
D y=split(x.second,en-sta+1);
maintain_flip(y.first);
root=merge(x.first,merge(y.first,y.second));
return ;
}
inline void write_in_order(Treap* pos){
if(pos==null) return;
pushdown(pos);
write_in_order(pos->son[0]);
write(pos->val);putchar(' ');
write_in_order(pos->son[1]);
return ;
}
signed main(){
n=read();m=read();
root=build();
while(m--) reverse();
write_in_order(root);
return 0;
}
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;
inline int read(){
int i=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch);ch=getchar())
if(ch=='-') f=-1;
for(;isdigit(ch);ch=getchar())
i=(i<<3)+(i<<1)+(ch^48);
return i*f;
}
int buf[1024];
inline void write(int x){
if(!x){putchar('0');return ;}
if(x<0){putchar('-');x=-x;}
while(x){buf[++buf[0]]=x%10,x/=10;}
while(buf[0]) putchar(buf[buf[0]--]+48);
return ;
}
typedef unsigned int uint;
inline uint nextUint() {
static uint seed = 19260817;
seed ^= seed << 13;
seed ^= seed >> 17;
seed ^= seed << 5;
return seed;
}
#define stan 88888
#define sten 11
struct Treap{
Treap* son[2];
Treap* fa;
int sze,val;
uint weight;
Treap(){val=-999999999,sze=0,weight=nextUint(),son[0]=son[1]=fa=this;}
inline void update(){
sze=son[0]->sze+son[1]->sze+1;
son[0]->fa=son[1]->fa=this;
}
}*null=new Treap(),*root=null,*stack[stan],*x,*last,*posi[stan];
typedef pair<Treap*,Treap*> D;
int n,m,s,u,a[stan];
char ord[sten];
inline Treap* newtreap(int val){
Treap* pos=new Treap();
pos->son[1]=pos->son[0]=pos->fa=null;
pos->sze=1;pos->val=val;pos->weight=nextUint();
return pos;
}
inline Treap* merge(Treap* a,Treap* b){
if(a==null) return b;
if(b==null) return a;
if(a->weight<b->weight){
a->son[1]=merge(a->son[1],b);
a->update();
return a;
}else{
b->son[0]=merge(a,b->son[0]);
b->update();
return b;
}
}
inline D split(Treap* pos,int k){
if(pos==null) return D(null,null);
D y;
if(pos->son[0]->sze>=k){
y=split(pos->son[0],k);
pos->son[0]=y.second;
pos->update();
y.second=pos;
}else{
y=split(pos->son[1],k-1-pos->son[0]->sze);
pos->son[1]=y.first;
pos->update();
y.first=pos;
}
return y;
}
inline Treap* build(){
int p=0;
for(int i=1;i<=n;++i){
a[i]=read();
posi[a[i]]=x=newtreap(a[i]);last=null;
while(p&&stack[p]->weight>x->weight){
stack[p]->update();
last=stack[p];
stack[p--]=null;
}
if(p) stack[p]->son[1]=x;x->fa=stack[p];
x->son[0]=last;last->fa=x;
stack[++p]=x;
}
while(p) stack[p--]->update();
return stack[1];
}
inline int getrank(Treap* pos){
int ret=pos->son[0]->sze;
while(pos->fa!=null&&pos->fa!=NULL){
if(pos==pos->fa->son[1]) ret+=pos->fa->son[0]->sze+1;
pos=pos->fa;
}
return ret;
}
inline void addhead(int x){
int k=getrank(posi[x]);
D X=split(root,k);
D y=split(X.second,1);
root=merge(y.first,merge(X.first,y.second));
return ;
}
inline void addtail(int x){
int k=getrank(posi[x]);
D X=split(root,k);
D y=split(X.second,1);
root=merge(X.first,merge(y.second,y.first));
return ;
}
void addmid(int xxx,int opt) {
if(!opt) return ;
static D X,y,z;
int k=getrank(posi[xxx]);
X=split(root, k);
y=split(X.second,1);
if(opt==-1)X=split(X.first,X.first->sze-1), root=merge(merge(X.first,y.first),merge(X.second,y.second));
else z=split(y.second,1), root=merge(merge(X.first,z.first),merge(y.first,z.second));
}
inline int getkth(int k){
D X=split(root,k-1);
D y=split(X.second,1);
Treap* pos=y.first;
root=merge(X.first,merge(pos,y.second));
return pos->val;
}
signed main(){
n=read();m=read();
root=build();
while(m--){
scanf("%s",ord);s=read();
switch(ord[0]){
case 'T':addhead(s);break;
case 'B':addtail(s);break;
case 'I':u=read();addmid(s,u);break;
case 'A':write(getrank(posi[s]));puts("");break;
case 'Q':write(getkth(s));puts("");break;
}
}
return 0;
}
(5)CQOI2014排序機械臂
題面還是有連結就是這裡
很中規中矩的不固定排列序+區間修改
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;
inline int read(){
int i=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch);ch=getchar())
if(ch=='-') f=-1;
for(;isdigit(ch);ch=getchar())
i=(i<<3)+(i<<1)+(ch^48);
return i*f;
}
int buf[1024];
inline void write(int x){
if(!x){putchar('0');return ;}
if(x<0){putchar('-');x=-x;}
while(x){buf[++buf[0]]=x%10,x/=10;}
while(buf[0]) putchar(buf[buf[0]--]+48);
return ;
}
typedef unsigned int uint;
inline uint nxtunit(){
static uint seed=19260817;
seed^=seed<<13;
seed^=seed>>17;
seed^=seed<<5;
return seed;
}
#define stan 111111
struct Treap{
Treap* son[2];
Treap* fa;
int sze,val;
uint weight;
bool flip;
Treap(){val=-999999999,sze=0,weight=nxtunit(),son[0]=son[1]=fa=this;flip=false;}
inline void update(){
sze=son[0]->sze+son[1]->sze+1;
son[0]->fa=son[1]->fa=this;
}
}*null=new Treap(),*root=null,*stack[stan],*x,*last,*posi[stan];
typedef pair<Treap*,Treap*> D;
int n,m,s,u,a[stan],to[stan];
struct thing{
int ord,val;
}thi[stan];
inline bool cmp(const thing &a,const thing &b){
if(a.val!=b.val) return a.val<b.val;
else return a.ord<b.ord;
}
inline Treap* newtreap(int val){
Treap* pos=new Treap();
pos->son[1]=pos->son[0]=pos->fa=null;
pos->sze=1;pos->val=val;pos->weight=nxtunit();
pos->flip=false;
return pos;
}
inline void pushdown(Treap* pos){
if(pos==null||pos==NULL) return ;
if(pos->flip){
pos->flip^=1;
pos->son[0]->flip^=1;
pos->son[1]->flip^=1;
swap(pos->son[0],pos->son[1]);
}
return ;
}
inline Treap* merge(Treap* a,Treap* b){
if(a==null) return b;
if(b==null) return a;
pushdown(a);pushdown(b);
if(a->weight<b->weight){
a->son[1]=merge(a->son[1],b);
a->update();
return a;
}else{
b->son[0]=merge(a,b->son[0]);
b->update();
return b;
}
}
inline D split(Treap* pos,int k){
if(pos==null) return D(null,null);
D y;pushdown(pos);
if(pos->son[0]->sze>=k){
y=split(pos->son[0],k);
pos->son[0]=y.second;
pos->update();
y.second=pos;
}else{
y=split(pos->son[1],k-1-pos->son[0]->sze);
pos->son[1]=y.first;
pos->update();
y.first=pos;
}
return y;
}
inline void rotate(Treap* pos){
if(pos==null||pos==NULL) return ;
rotate(pos->fa);
pushdown(pos);
return ;
}
inline int getrank(Treap* pos){
rotate(pos);
int ret=pos->son[0]->sze+1;
while(pos->fa!=null&&pos->fa!=NULL){
if(pos==pos->fa->son[1]) ret+=pos->fa->son[0]->sze+1;
pos=pos->fa;
}
return ret;
}
inline int getans(int d){
int ret=getrank(posi[d]);
D x=split(root,ret);
D y=split(x.first,d-1);
y.second->flip^=1;
root=merge(merge(y.first,y.second),x.second);
return ret;
}
signed main(){
n=read();
for(int i=1;i<=n;++i){
a[i]=read();thi[i].ord=i;thi[i].val=a[i];
}
sort(thi+1,thi+n+1,cmp);
for(int i=1;i<=n;++i)
to[thi[i].ord]=i;
for(int i=1;i<=n;++i){
posi[to[i]]=newtreap(a[i]);
root=merge(root,posi[to[i]]);
}
for(int i=