splay死循环,玄关求条,问题出在左右旋
查看原帖
splay死循环,玄关求条,问题出在左右旋
637073
wujingfey楼主2024/11/11 21:35
/*
P3369 splay平衡树
一种数据结构,支持插入、删除、查询排名、查询前驱后继

右旋操作:
u上移替代 fa,保留左儿子,丢掉右儿子 
fa 下移,保留 fa 原本的右儿子,同时自己变成 u 的右儿子 
fa 的左儿子替换成 u 的右儿子 

总而言之:
儿子变父亲,儿子的右儿子变左孙子 

左旋同理
----------------------------
伸展操作
 
如果 xyz 是直线型,则连续旋转两次 x
如果 xyz 是折线型,则先下沉 y 再旋转 x 
*/
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
struct NODE{
	int s[2],fa;//节点联系 
	int val,cnt,sz;//节点信息 
	void init(int f,int v){//初始化 
		fa=f, val=v;
		cnt=sz=1;
	}
}tr[N];
int rt,idx;
void push_up(int p){
	tr[p].sz=tr[tr[p].s[0]].sz+tr[tr[p].s[1]].sz+tr[p].cnt;
}
void rotate(int x){//旋转 
	int y=tr[x].fa, z=tr[y].fa;
	if(x==tr[y].s[0]){//右旋 
		//z儿子从 x 变成 y 
		tr[x].fa=z;
		if(y==tr[z].s[0]) tr[z].s[0]=x;
		else tr[z].s[1]=x;
		//x儿子过继给y 
		tr[y].s[0]=tr[x].s[1];
		//xy关系互换 
		tr[x].s[1]=y;
		tr[y].fa=x;
	}else{//左旋 
		tr[x].fa=z;
		if(y==tr[z].s[0]) tr[z].s[0]=x;
		else tr[z].s[1]=x;
		tr[y].s[1]=tr[x].s[0];
		tr[x].s[0]=y;
		tr[y].fa=x;
	}
	push_up(y), push_up(x);//从下向上push_up 
}
void splay(int x,int k){
	//k > 0时把 x 旋转到 x下面
	//k = 0时把 x 旋转到 rt 
	while(tr[x].fa!=k){
		int y=tr[x].fa, z=tr[y].fa;
		if(z!=k){
			if((tr[y].s[0]==x)^(tr[z].s[0]==y)) rotate(x);//折线型 
			else rotate(y);//直线形 
			//折转底,直转中 
		}
		rotate(x);
	}
	if(k==0) rt=x;//更新根节点 
} 
void find(int v){//找到 v,并把 v 旋转到 rt 
	int x=rt;//x表示 v 的位置 
	while(tr[x].s[v>tr[x].val] && v!=tr[x].val){
		x=tr[x].s[v>tr[x].val];
		//tr[x].s[v>tr[x].val] 指向的就是 v 所在的子树 
		//如果找不到 v,就找最靠近的小于v的点 
	}
	splay(x,0);
} 
int get_pre(int v){//v 的前驱编号 
	find(v);//把 v 转移到 rt 上 
	int x=rt;
	if(tr[x].val<v) return x; //相当于v不存在
	x=tr[x].s[0];
	while(tr[x].s[1]) x=tr[x].s[1];//尽可能靠右走,这样会更大
	return x; 
}
int get_suc(int v){
	find(v);
	int x=rt;
	if(tr[x].val>v) return x;
	x=tr[x].s[1];
	while(tr[x].s[0]) x=tr[x].s[0];
	return x;
}
void del(int v){//删除 
	int pre=get_pre(v);
	int suc=get_suc(v);
	splay(pre,0);//pre旋到rt 
	splay(suc,pre); //suc旋到pre下面
	int del=tr[suc].s[0];
	//画图可知删除点是后继的 ls
	//可以证明这样旋转,del一定会被转到叶子 
	if(tr[del].cnt>1){
		tr[del].cnt--;
		splay(del,0);
	}else{
		tr[suc].s[0]=0;//清空连边 
		splay(suc,0);
	}
}
void insert(int v){
	int x=rt, p=0;
	while(x && tr[x].val!=v){
		p=x;//p就是最末端的点 
		x=tr[x].s[v>tr[x].val];//x不断下找直到找对应值 
	}
	if(x) tr[x].cnt++;//存在 
	else{//不存在 
		x=++idx;
		tr[p].s[v>tr[p].val]=x;
		tr[x].init(p,v);//初始化 
	}
	splay(x,0); 
}
int get_rk(int v){ //排名
  	insert(v);
  	int res=tr[tr[rt].s[0]].sz;
  	del(v);
  	return res;
}
int get_val(int k){//求排名k的值 (记得+1后再找,因为存在-INF) 
	int x=rt;
	while(1){
		int y=tr[x].s[0];
		if(tr[y].sz+tr[x].cnt < k){//rs 
			k-=tr[y].sz+tr[x].cnt;
			x=tr[x].s[1];
		}else{//ls或者 x 
			if(k<=tr[y].sz) x=tr[x].s[0];//还在ls 
			else break;//就在当前点 
		}
	}
	splay(x,0);
	return tr[x].val;
}
int n;
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int INF=1e9+7;
	insert(-INF),insert(INF);
	cin>>n;
	while(n--){
		int op,x;
		cin>>op>>x;
		if(op==1) insert(x);
		if(op==2) del(x);
		if(op==3) cout<<get_rk(x)<<'\n';
		if(op==4) cout<<get_val(x+1)<<'\n';//记得+1
		if(op==5) cout<<tr[get_pre(x)].val<<'\n';
		if(op==6) cout<<tr[get_suc(x)].val<<'\n'; 
	}
	return 0;
}
/*
Hack
20
1 964673
5 968705
4 1
3 964673
5 965257
1 915269
1 53283
3 964673
3 53283//死循环位置
3 53283
1 162641
5 973984
1 948119
2 915269
2 53283
6 959161
1 531821
1 967521
2 531821
1 343410
*/
2024/11/11 21:35
加载中...