suxxsfe

一言(ヒトコト)

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 *&ltree,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 *&ltree,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;
}
posted @ 2020-08-07 15:30  suxxsfe  阅读(246)  评论(0编辑  收藏  举报