平衡树

Splay

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+1e5;
struct splay{
	int fa,ch[2],val,cnt,siz;
}t[maxn];
int root,tot,xx,n,opt,m;
inline void update(int x){//更新子树大小 
	t[x].siz=t[t[x].ch[0]].siz+t[t[x].ch[1]].siz+t[x].cnt;
}
inline void rotate(int x){//旋转,交换该节点及其父亲节点的位置并维护平衡树性质  左旋拎右左挂右,右旋拎左右挂左 
	int y=t[x].fa;//x的父亲 
	int z=t[y].fa;//x的爷爷 
	int k=t[y].ch[1]==x;//x相对y的位置,0为左儿子,1为右儿子 
	t[z].ch[t[z].ch[1]==y]=x;//把z的y儿子所处位置变为x 
	t[x].fa=z;//x父亲变为z 
	t[y].ch[k]=t[x].ch[k^1];// 原x所在y的位置更新为不与y在同一链上的x的儿子 
	t[t[x].ch[k^1]].fa=y;
	t[x].ch[k^1]=y;//y成为x与原位置关系相反的儿子 
	t[y].fa=x;
	update(y);//更新 
	update(x);
}
inline void splay(int x,int king){//伸展,将x转到king节点的左或右儿子上 
	while(t[x].fa!=king){//转到x成为king的儿子 
		int y=t[x].fa,z=t[y].fa;
		if(z!=king){//祖父非king 
			if((t[z].ch[0]==y)^(t[y].ch[0]==x)) rotate(x);//父亲和祖父方向不一致,+line32共转自己两次 
			else rotate(y);//父亲祖父方向一致,+line32先转父亲再转祖父 
			//等效(t[z].ch[0]==y)^(t[y].ch[0]==x)?rotate(x):rotate(y);
		}
		rotate(x);//祖父是king只转自己一次,或同上 
	}
	if(king==0) root=x;//如果king是0,将根节点赋值为x 
}
inline void find(int x){//找元素并转到根节点 
	int u=root;
	if(!u) return;//树是空的 
	while(t[u].ch[x>t[u].val]&&x!=t[u].val) //当存在儿子且当前位置值不等于x 
		u=t[u].ch[x>t[u].val];//跳到儿子,找x的父节点 
	splay(u,0);
}
inline void insert(int x){
	int u=root,fa=0;
	while(u&&t[u].val!=x){//存在u且不是当前位置的值 
		fa=u;//向下u的儿子,父节点变为u 
		u=t[u].ch[x>t[u].val];//比u大往右找,否则往左找 
	}
	if(u) t[u].cnt++;//存在u,该位置数量+1 
	else{//不存在就新增节点 
		u=++tot;
		if(fa) t[fa].ch[x>t[fa].val]=u;//父节点非根 
		t[u].ch[0]=t[u].ch[1]=0;
		t[tot].fa=fa;
		t[tot].val=x;
		t[tot].cnt=1;
		t[tot].siz=1;
	}
	splay(u,0);//保证平衡 
}
inline int next(int x,int f){//前驱0,后继1 
	find(x);//找到x并延伸到根节点 
	int u=root;//若x存在根节点即为其父 
	if(t[u].val>x&&f) return u;//当前节点值大于x且要查后继 
	if(t[u].val<x&&!f) return u;//当前节点值小于x且要查前驱 
	u=t[u].ch[f];//后继右儿子,前驱左儿子 
	while(t[u].ch[f^1]) u=t[u].ch[f^1];//反着跳转
	splay(u,0); 
	return u;
}
inline void Delete(int x){
	int last=next(x,0);//找前驱 
	int nxt=next(x,1);//找后继 
	splay(last,0);//前驱转到根 
	splay(nxt,last);//后继转到根节点下面 
	int del=t[nxt].ch[0];//x为后继的左儿子 
	if(t[del].cnt>1){
		t[del].cnt--;
		splay(del,0);
	}
	else t[nxt].ch[0]=0;//删节点 
}
inline int kth(int x){
	int u=root;
	if(t[u].siz<x) return 0;//树上点不够 
	while(1){
		int y=t[u].ch[0];//左儿子 
		if(x>t[y].siz+t[u].cnt){//左儿子和该节点大小不够 
			x-=t[y].siz+t[u].cnt; 
			u=t[u].ch[1];//在右儿子上找 
		}
		else if(t[y].siz>=x) u=y;
		else{
			splay(u,0); 
			return t[u].val;
		} 		
	}
	splay(u,0);
}
int main(){
std::ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n;
	insert(1e9);//哨兵 
	insert(-1e9);
	for(int i=1;i<=n;i++){
		cin>>opt>>xx;
		switch(opt){
			case 1:insert(xx);break;
			case 2:Delete(xx);break;
			case 3:insert(xx);find(xx);cout<<t[t[root].ch[0]].siz<<'\n';Delete(xx);break;
			case 4:cout<<kth(xx+1)<<'\n';break;
			case 5:insert(xx);cout<<t[next(xx,0)].val<<'\n';Delete(xx);break;
			case 6:insert(xx);cout<<t[next(xx,1)].val<<'\n';Delete(xx);break;
		}
	}
	return 0;
}

posted @ 2025-08-09 15:36  _dlwlrma  阅读(8)  评论(0)    收藏  举报