fhq-treap,splay 模板
阿新 • • 發佈:2020-08-07
這兩個一般都可以用來處理區間問題
實測 fhq-treap 比 splay 常數更大一些
普通平衡樹:https://www.luogu.com.cn/problem/P3369
fhq-treap,這裡分裂寫的是按大小分裂
#include<cstdio> #include<algorithm> #include<iostream> #include<cmath> #include<ctime> #include<iomanip> #include<cstring> #define reg register #define EN std::puts("") #define LL long long inline int read(){ register int x=0;register int y=1; register char c=std::getchar(); while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();} while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();} return y?x:-x; } struct tr{ tr *ls,*rs; int val,rnd,size; }*root; inline void pushup(tr *tree){ tree->size=1+(tree->ls?tree->ls->size:0)+(tree->rs?tree->rs->size:0); } void split(tr *tree,int k,tr *<ree,tr *&rtree){ if(tree==NULL) return ltree=rtree=NULL,void(); tr *tmp; int ls_size=tree->ls?tree->ls->size:0; if(ls_size>=k){ split(tree->ls,k,ltree,tmp); rtree=tree;rtree->ls=tmp; pushup(rtree); } else{ split(tree->rs,k-ls_size-1,tmp,rtree); ltree=tree;ltree->rs=tmp; pushup(ltree); } } tr* merge(tr *ltree,tr *rtree){ if(!ltree||!rtree) return ltree?ltree:rtree; if(ltree->rnd<rtree->rnd){ ltree->rs=merge(ltree->rs,rtree); pushup(ltree); return ltree; } else{ rtree->ls=merge(ltree,rtree->ls); pushup(rtree); return rtree; } } inline int rank(int val){ reg int ans=1; reg tr *tree=root; while(tree){ if(val<=tree->val) tree=tree->ls; else{ ans+=(tree->ls?tree->ls->size:0)+1; tree=tree->rs; } } return ans; } inline void insert(int val){ int rk=rank(val)-1; tr *x,*y; split(root,rk,x,y); tr *new_=new tr; new_->size=1; new_->val=val;new_->rnd=rand(); new_->ls=new_->rs=NULL; root=merge(merge(x,new_),y); } inline void del(int val){ int rk=rank(val); tr *x,*y,*z; split(root,rk,x,z); split(x,rk-1,x,y);//y->val=val,y->size=1 root=merge(x,z); delete y; } inline int kth(int k){ tr *x,*y,*z; split(root,k-1,x,y); split(y,1,y,z); root=merge(x,merge(y,z)); return y->val; } int main(){ srand(time(0)); int n=read();reg int op,x;while(n--){ op=read();x=read(); if(op==1) insert(x); else if(op==2) del(x); else if(op==3) printf("%d\n",rank(x)); else if(op==4) printf("%d\n",kth(x)); else if(op==5) printf("%d\n",kth(rank(x)-1)); else printf("%d\n",kth(rank(x+1))); } return 0; }
splay,維護父指標的版本,https://www.bilibili.com/video/BV1wt411u7xL
放張維基上摘下來的圖:
一字型,是兩次同方向的旋轉,之字形是兩次不同方向的
#include<cstdio> #include<algorithm> #include<iostream> #include<cmath> #include<map> #include<iomanip> #include<cstring> #define reg register #define EN std::puts("") #define LL long long inline int read(){ register int x=0;register int y=1; register char c=std::getchar(); while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();} while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();} return y?x:-x; } #define N 100005 struct SPLAY{ struct tr{ tr *son[2],*fa; int val,cnt,size; }*root,*null,dizhi[N]; int tot; inline int ident(tr *tree,tr *fa){return fa->son[1]==tree;}//0:tree is leftson of fa,1:right inline void connect(tr *tree,tr *fa,int k){//將 tree 連成 fa 的兒子,k 確定左兒子還是右兒子 fa->son[k]=tree;tree->fa=fa; } inline void update(tr *tree){tree->size=tree->son[0]->size+tree->son[1]->size+tree->cnt;} inline void rotate(tr *tree){//tree 要旋轉的兒子節點 tr *fa=tree->fa,*faa=fa->fa;int k=ident(tree,fa); connect(tree->son[k^1],fa,k); connect(tree,faa,ident(fa,faa)); connect(fa,tree,k^1); update(fa);update(tree); } inline void splay(tr *x,tr *top){//x 轉到 top 的兒子(具體是左或有根據 x 的位置) reg tr*fa,*faa; if(top==null) root=x; //改 root 指標,如果不寫這句,只是把 x 轉到了 root 的位置,但 root 這個指標還並沒有指向 x while(x->fa!=top){ fa=x->fa;faa=fa->fa; if(faa!=top) ident(fa,faa)^ident(x,fa)?rotate(x):rotate(fa); //相同異或得零,一字型,以 fa 為兒子轉,否則以 x 為兒子 rotate(x);//第二次一定是以 x 為兒子 } // if(root->fa!=null) puts("WTF???"); } void del(tr *tree,int val){ if(val==tree->val){ splay(tree,null); if(tree->cnt>1) tree->cnt--,tree->size--; else if(tree->son[1]==null) root=root->son[0],root->fa=null;//更新 root 的 fa else{ tr *p=tree->son[1]; while(p->son[0]!=null) p=p->son[0]; splay(p,tree); connect(tree->son[0],p,0); root=p; root->fa=null; update(root); } } else del(tree->son[val>tree->val],val); } void insert(tr *&tree,int val,tr *fa){ if(tree==null){ tree=&dizhi[++tot]; tree->son[0]=tree->son[1]=null; tree->cnt=tree->size=1; tree->val=val; tree->fa=fa; splay(tree,null); return; } if(val==tree->val) tree->size++,tree->cnt++,splay(tree,null); else insert(tree->son[val>tree->val],val,tree); } int rank(reg int val){ reg tr *tree=root;int ans=1; while(tree!=null){ if(tree->val==val){ ans+=tree->son[0]->size; splay(tree,null);return ans; } if(val<tree->val) tree=tree->son[0]; else ans+=tree->son[0]->size+tree->cnt,tree=tree->son[1]; } return ans; } int kth(reg int rank){ reg tr *tree=root; while(tree!=null){ if(rank>tree->son[0]->size&&rank<=tree->son[0]->size+tree->cnt){ splay(tree,null);return tree->val; } if(rank<=tree->son[0]->size) tree=tree->son[0]; else rank-=tree->son[0]->size+tree->cnt,tree=tree->son[1]; } } inline void init(){ root=null=&dizhi[0]; root->fa=null; } }splay; int main(){ // std::freopen("P3369_6.in","r",stdin); // std::freopen("out","w",stdout); int n=read(); reg int op,x;splay.init(); while(n--){ op=read();x=read(); if(op==1) splay.insert(splay.root,x,splay.null); else if(op==2) splay.del(splay.root,x); else if(op==3) printf("%d\n",splay.rank(x)); else if(op==4) printf("%d\n",splay.kth(x)); else if(op==5) printf("%d\n",splay.kth(splay.rank(x)-1)); else printf("%d\n",splay.kth(splay.rank(x+1))); } return 0; }
文藝平衡樹:https://www.luogu.com.cn/problem/P3391
splay,這裡 splay 不再是一個二叉權值查詢樹了,每個節點維護的 val 對應原序列中的一個元素,但是並不保證左兒子的 val 都比他小,右兒子的都比他大
那麼,一個節點左兒子的大小加一,就是它在原數裡中的下標
然後每次通過把 \(l-1\),轉到根節點,然後 \(r+1\) 轉到根節點的右子樹,這樣,\([l-1,r+1]\) 的所有數就都在根節點的右兒子的左兒子裡了,從這裡打懶標記,記得下傳
還得插入一個極大值和極小值
一開始想寫了個不維護父指標的,結果發現不能比較權值而是得比較左子樹兒子大小,很是麻煩,口胡失敗,所以還是寫的維護父指標
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
register int x=0;register int y=1;
register char c=std::getchar();
while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
return y?x:-x;
}
struct SPLAY{
struct tr{
tr *son[2],*fa;
int val,size;
int rev;
}*root,*null,dizhi[100005];
int tot;
inline void update(tr *tree){tree->size=1+tree->son[0]->size+tree->son[1]->size;}
inline void pushdown(tr *tree){
if(!tree->rev||tree==null) return;
std::swap(tree->son[0],tree->son[1]);
tree->son[0]->rev^=1;tree->son[1]->rev^=1;
tree->rev=0;
}
inline int ident(tr *tree,tr *fa){return fa->son[1]==tree;}
inline void connect(tr *tree,tr *fa,int k){fa->son[k]=tree;tree->fa=fa;}
inline void rotate(tr *tree){
tr *fa=tree->fa,*faa=fa->fa;
pushdown(fa);pushdown(tree);
int k=ident(tree,fa);
connect(tree->son[k^1],fa,k);
connect(fa,tree,k^1);
connect(tree,faa,ident(fa,faa));
update(fa);update(tree);
}
void splay(tr *tree,tr *top){
reg tr *fa,*faa;
if(top==null) pushdown(root),root=tree;
while(tree->fa!=top){
fa=tree->fa;faa=fa->fa;
if(faa!=top) ident(fa,faa)^ident(tree,fa)?rotate(tree):rotate(fa);
rotate(tree);
}
}
void insert(tr *&tree,int val,tr *fa){
if(tree==null){
tree=&dizhi[++tot];
tree->val=val;tree->fa=fa;
tree->son[0]=tree->son[1]=null;
tree->size=1;
splay(tree,null);
return;
}
insert(tree->son[val>tree->val],val,tree);
}
inline tr *get_node(int rank){
reg tr *tree=root;
while(tree!=null){
pushdown(tree);
if(rank==tree->son[0]->size+1){
splay(tree,null);return tree;
}
if(rank<=tree->son[0]->size) tree=tree->son[0];
else rank-=tree->son[0]->size+1,tree=tree->son[1];
}
}
inline void rev(int l,int r){
tr *L=get_node(l-1),*R=get_node(r+1);
//再後面兩個 splay 操作前先獲取這兩個節點
//如果直接把函式值當作引數往後面的 splay 傳的話,第二個 get_node 裡的 splay 操作會改變樹結構
//出現錯誤
splay(L,null);//root->rs:[l,n]
splay(R,root);//root->rs->ls:[l,r]
root->son[1]->son[0]->rev^=1;
}
void dfs(tr *tree){
pushdown(tree);
if(tree->son[0]!=null) dfs(tree->son[0]);
if(tree->val!=-1&&tree->val!=1e9) printf("%d ",tree->val);
if(tree->son[1]!=null) dfs(tree->son[1]);
}
}splay;
int main(){
int n=read(),m=read();
splay.root=splay.null=&splay.dizhi[0];
splay.root->fa=splay.null;
splay.insert(splay.root,-1,splay.null);
splay.insert(splay.root,1e9,splay.null);
for(reg int i=1;i<=n;i++)
splay.insert(splay.root,i,splay.null);
reg int l,r;while(m--){
l=read()+1;r=read()+1;
splay.rev(l,r);
}
splay.dfs(splay.root);
return 0;
}
fhq-treap,就是把每次要處理的那個區間的樹分裂出來,打懶標記
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<ctime>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
register int x=0;register int y=1;
register char c=std::getchar();
while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
return y?x:-x;
}
struct tr{
tr *ls,*rs;
int val,rnd;
int size,rev;
}*root;
inline void pushup(tr *tree){
tree->size=1+(tree->ls?tree->ls->size:0)+(tree->rs?tree->rs->size:0);
}
inline void pushdown(tr *tree){
if(!tree->rev) return;
std::swap(tree->ls,tree->rs);
if(tree->ls) tree->ls->rev^=1;
if(tree->rs) tree->rs->rev^=1;
tree->rev=0;
}
void split(tr *tree,int size,tr *<ree,tr *&rtree){
if(!tree) return ltree=rtree=NULL,void();
tr *tmp;
pushdown(tree);
int lssize=tree->ls?tree->ls->size:0;
if(lssize<size){
split(tree->rs,size-lssize-1,tmp,rtree);
ltree=tree;ltree->rs=tmp;
pushup(ltree);
}
else{
split(tree->ls,size,ltree,tmp);
rtree=tree;rtree->ls=tmp;
pushup(rtree);
}
}
tr* merge(tr *ltree,tr *rtree){
if(!ltree) return rtree;
if(!rtree) return ltree;
if(ltree->rnd<rtree->rnd){
pushdown(ltree);
ltree->rs=merge(ltree->rs,rtree);
pushup(ltree);return ltree;
}
else{
pushdown(rtree);
rtree->ls=merge(ltree,rtree->ls);
pushup(rtree);return rtree;
}
}
inline void reverse(int l,int r){
tr *x,*y,*z;
split(root,l-1,x,y);
split(y,r-l+1,y,z);
y->rev^=1;
root=merge(merge(x,y),z);
}
void dfs(tr *tree){
pushdown(tree);
if(tree->ls) dfs(tree->ls);
printf("%d ",tree->val);
if(tree->rs) dfs(tree->rs);
}
int main(){
srand(time(0));
int n=read(),m=read();
for(reg int i=1;i<=n;i++){
tr *new_=new tr;
new_->size=1;
new_->val=i;new_->rnd=rand();
new_->ls=new_->rs=NULL;
root=merge(root,new_);
}
reg int l,r;while(m--){
l=read();r=read();
reverse(l,r);
}
dfs(root);
return 0;
}