fhq-treap,splay 模板
这两个一般都可以用来处理区间问题
实测 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
大佬的博客:https://www.luogu.com.cn/blog/tiger0132/slay-notes
放张维基上摘下来的图:
可以发先每次旋转后,splay 的合法性和中序遍历是不变的
splay 是双旋的,一字型,是两次同方向的旋转,之字形是两次不同方向的
#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();
pushdown(tree);
int lssize=tree->ls?tree->ls->size:0;
if(lssize<size){
ltree=tree;
split(tree->rs,size-lssize-1,ltree->rs,rtree);
pushup(ltree);
}
else{
rtree=tree;
split(tree->ls,size,ltree,rtree->ls);
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;
}