首页 > 其他 > 详细

splay板子题

时间:2019-05-17 20:45:44      阅读:168      评论:0      收藏:0      [点我收藏+]

https://www.luogu.org/problemnew/show/P3369

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int sum=0,x=1;
	char ch=getchar();
	while(ch<‘0‘||ch>‘9‘){
		if(ch==‘-‘)
			x=0;
		ch=getchar();
	}
	while(ch>=‘0‘&&ch<=‘9‘)
		sum=(sum<<1)+(sum<<3)+(ch^48),ch=getchar();
	return x?sum:-sum;
}
inline void write(int x){
	if(x<0)
		putchar(‘-‘),x=-x;
	if(x>9)
	 	write(x/10);
	putchar(x%10+‘0‘);
}
const int M=2e5+5;
const int inf=0x3f3f3f3f;
int ch[M][2],val[M],cnt[M],fa[M],sz[M],ncnt,root;
int  ck(int x){
	return ch[fa[x]][1]==x;
}
void up(int x){
	sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+cnt[x];
}
void rotate(int x){
	int y=fa[x];
	int z=fa[y];
	int k=ck(x);
	int w=ch[x][k^1];
	ch[y][k]=w,fa[w]=y;
	ch[z][ck(y)]=x,fa[x]=z;
	ch[x][k^1]=y,fa[y]=x;
	up(y),up(x);
}
void splay(int x,int goal=0){//将x旋转为goal的儿子,如果goal是0则旋转到根
	while(fa[x]!=goal){//一直旋转到x成为goal的儿子
		int y=fa[x],z=fa[y];//父节点祖父节点
		if(z!=goal)//如果Y不是根节点,则分为两类来旋转(如果随意旋转会达不到查询效果) 
			ck(x)^ck(y)?rotate(x):rotate(y);
		rotate(x);//无论怎么样最后的一个操作都是旋转x
	}
	if(!goal)
		root=x;//如果goal是0,则将根节点更新为x
}
void find(int x){//查找x的位置,并将其旋转到根节点 
	if(!root)//树空 
		return ;
	int cur=root;
	while(ch[cur][x>val[cur]]&&x!=val[cur])//当存在儿子并且当前位置的值不等于x 
		cur=ch[cur][x>val[cur]];//跳转到儿子,查找x的父节点
	splay(cur);//把当前位置旋转到根节点
}
void insertt(int x){//插入x
	int cur=root,p=0;//当前位置cur,cur的父节点p 
	while(cur&&val[cur]!=x){//当u存在并且没有移动到当前的值
		p=cur;//向下u的儿子,父节点变为u
		cur=ch[cur][x>val[cur]];//大于当前位置则向右找,否则向左找
	}
	if(cur)//存在这个值的位置
		cnt[cur]++;//增加一个数
	else{//不存在这个数字,要新建一个节点来存放
		cur=++ncnt;//新节点的位置
		if(p)//如果父节点非根
			ch[p][x>val[p]]=cur;//不存在儿子
		fa[cur]=p;//父节点
		ch[cur][0]=ch[cur][1]=0;
		val[cur]=x;//值
		cnt[cur]=sz[cur]=1;//数量,大小 
	}
	splay(cur);//把当前位置移到根,保证结构的平衡。注意前面因为更改了子树大小,所以这里必须Splay上去进行pushup保证size的正确。
}
int kth(int k){
	int u=root;
	while(true){
		if(ch[u][0]&&k<=sz[ch[u][0]])
			u=ch[u][0];
		else if(k>sz[ch[u][0]]+cnt[u])
			k-=sz[ch[u][0]]+cnt[u],u=ch[u][1];
		else
			return u;	
	}
}
int  pre(int x){//查找x的前驱(0)或者后继(1)
	find(x);
	if(val[root]<x)//如果当前节点的值小于x并且要查找的是前驱
		return root;
	int u=ch[root][0];//前驱再左儿子上找 
	while(ch[u][1])
		u=ch[u][1];
	return u;//返回位置 
}
int succ(int x){
	find(x);
	if(val[root]>x)//如果当前节点的值大于x并且要查找的是后继
		return root;
	int u=ch[root][1];//后继在右儿子上找 
	while(ch[u][0])
		u=ch[u][0];
	return u;
}
void remove(int x){
	int las=pre(x),nex=succ(x);
	splay(las,0),splay(nex,las);
	//将前驱旋转到根节点,后继旋转到根节点下面
    //很明显,此时后继是前驱的右儿子,x是后继的左儿子,并且x是叶子节点
	int del=ch[nex][0];//后继的左儿子
	if(cnt[del]>1)//如果超过一个
		cnt[del]--,splay(del,0);//直接减少一个
	else
		ch[nex][0]=0;//这个节点直接丢掉(不存在了)
}
void init(){
	memset(ch,0,sizeof(cnt));
	memset(cnt,0,sizeof(cnt));
	memset(sz,0,sizeof(sz));
	memset(val,0,sizeof(val));
	memset(fa,0,sizeof(fa));
	ncnt=root=0;
}
int main(){
	
	
	int n;
	while(~scanf("%d",&n)){
		init();
		insertt(inf);
		insertt(-inf);
		while(n--){
			
			int op=read(),x=read();
			if(op==1)
				insertt(x);
			else if(op==2)
				remove(x);
			else if(op==3)
				find(x),write(sz[ch[root][0]]),putchar(‘\n‘);
			else if(op==4)
				write(val[kth(x+1)]),putchar(‘\n‘);
			else if(op==5)
				write(val[pre(x)]),putchar(‘\n‘);
			else
				write(val[succ(x)]),putchar(‘\n‘);
		}
		putchar(‘\n‘);
	}
	return 0;
}

  

splay板子题

原文:https://www.cnblogs.com/starve/p/10883512.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!