BZOJ4825 [Hnoi2017]单旋

本文详细介绍了Splay树(伸展树)的数据结构及其在算法竞赛中的应用。通过具体实现展示了如何利用Splay树进行元素的插入、删除及查询操作,并结合平衡旋转等技巧保持树的平衡状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

HNOID1每道题都已小于2s的差距领先ljss交到BZ上23333

论快捷键提交的优越性

考虑一次插入了x,x的前驱为pre,后继为nxt,平衡树中排名相邻的两个点一定互为祖先关系,那么如果pre为nxt的祖先,x就插入到了nxt的左儿子,如果nxt为pre的祖先,x就插入到了pre的右儿子

考虑把最小值旋到根的操作,最小值没有左子树,最小值的右子树变成了最小值父亲的左子树,根变成了最小值的右儿子

把最大值旋到根同理

那么用ETT来维护这个spaly即可

ljss告诉我们不用ETT直接写splay来维护dfs序也行

#include<iostream>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<cstdlib>
#include<cstdio>
#include<map>
#include<bitset>
#include<set>
#include<stack>
#include<vector>
#include<queue>
using namespace std;
#define MAXN 400010
#define MAXM 1010
#define ll long long
#define eps 1e-8
#define MOD 1000000007
#define INF 1000000000
#define tr son[son[rt][1]][0]
char xB[1<<15],*xS=xB,*xTT=xB;
#define getc() (xS==xTT&&(xTT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xTT)?0:*xS++)
#define isd(c) (c>='0'&&c<='9')
int read(){
	char xchh;
	int xaa;
    while(xchh=getc(),!isd(xchh));(xaa=xchh-'0');
    while(xchh=getc(),isd(xchh))xaa=xaa*10+xchh-'0';return xaa;
}

int m;
map<int,int>L,R;
set<int>S;
int tot;
int fa[MAXN],son[MAXN][2],v[MAXN],sum[MAXN];
int rt;
void tpt(){
	int i;
	cerr<<rt<<"!!!"<<endl;
	for(i=1;i<=tot;i++){
		cerr<<i<<':'<<son[i][0]<<' '<<son[i][1]<<' '<<fa[i]<<'#'<<v[i]<<' '<<sum[i]<<endl;
	}
	cerr<<endl;
}
inline void ud(int x){
	sum[x]=v[x]+sum[son[x][0]]+sum[son[x][1]];
}
inline void cot(int x,int y,bool z){
	if(x){
		fa[x]=y;
	}
	if(y){
		son[y][z]=x;
	}
}
inline void rot(int x,bool z){
	int xx=fa[x],xxx=fa[xx];
	cot(son[x][z],xx,z^1);
	cot(x,xxx,son[xxx][1]==xx);
	cot(xx,x,z);
	ud(xx);
}
void splay(int x,int y){
	int xx=fa[x],xxx=fa[xx];
	while(xx!=y){
		if(xxx==y){
			rot(x,son[xx][0]==x);
		}else{
			bool z=son[xxx][0]==xx;
			if(son[xx][z]==x){
				rot(x,z^1);
				rot(x,z);
			}else{
				rot(xx,z);
				rot(x,z);
			}
		}
		xx=fa[x],xxx=fa[xx];
	}
	ud(x);
	if(!y){
		rt=x;
	}
}
void ins(){
	tr=tot-1;
	son[tr][1]=tot;
	fa[son[tr][1]]=tr;
	fa[tr]=son[rt][1];
	ud(son[tr][1]);
	ud(tr);
	ud(son[rt][1]);
	ud(rt);
}
void ins1(int x){
	splay(x,0);
	int t=son[rt][1];
	while(son[t][0]){
		t=son[t][0];
	}
	splay(t,rt);
	ins();
}
void ins2(int x){
	splay(x,0);
	int t=son[rt][0];
	while(son[t][1]){
		t=son[t][1];
	}
	splay(t,0);
	ins();
}
int ask(int x){
	splay(x,0);
	return sum[son[x][0]]+v[x];
}
void del(int x){
	splay(x,0);
	if(!son[x][0]){
		rt=son[x][1];
		fa[rt]=0;
		return ;
	}
	if(!son[x][1]){
		rt=son[x][0];
		fa[rt]=0;
		return ;
	}
	if(!son[x][0]&&!son[x][1]){
		rt=0;
		return ;
	}
	int t=son[x][0];
	while(son[t][1]){
		t=son[t][1];
	}
	splay(t,rt);
	son[t][1]=son[rt][1];
	fa[son[rt][1]]=t;
	ud(t);
	rt=t;
	fa[rt]=0;
}
void toins(int l,int r){
	son[l][0]=son[r][1]=0;
	son[r][0]=rt;
	son[l][1]=r;
	fa[rt]=r;
	fa[r]=l;
	ud(r);
	ud(l);
	rt=l;
	fa[rt]=0;
}
int main(){
	int o,x;
	set<int>::iterator it,itp,itn;
	m=read();
	while(m--){
		o=read();
		if(o==1){
			x=read();
			v[L[x]=++tot]=1;
			v[R[x]=++tot]=-1;
			it=(S.insert(x)).first;
			if(S.size()==1){
				rt=tot-1;
				fa[tot]=tot-1;
				son[tot-1][1]=tot;
				ud(tot);
				ud(tot-1);
			}else if(it==S.begin()){
				ins1(L[*(++it)]);
			}else if(it==(--S.end())){
				ins2(R[*(--it)]);
			}else{
				itn=itp=it;
				int pre=L[*(--itp)],nxt=L[*(++itn)];
				if(ask(pre)<ask(nxt)){
					ins2(nxt+1);
				}else{
					ins1(pre);
				}
			}
			printf("%d\n",ask(tot-1));
		}
		if(o==2){
			int l=L[*S.begin()],r=R[*S.begin()];
			
			printf("%d\n",ask(l));
			del(l);
			del(r);
			toins(l,r);
		}
		if(o==3){
			int l=L[*(--S.end())],r=R[*(--S.end())];
			printf("%d\n",ask(l));
			del(l);
			del(r);
			toins(l,r);
		}
		if(o==4){
			int l=L[*S.begin()],r=R[*S.begin()];
			S.erase(S.begin());
			printf("%d\n",ask(l));
			del(l);
			del(r);
		}
		if(o==5){
			int l=L[*(--S.end())],r=R[*(--S.end())];
			S.erase(--S.end());
			printf("%d\n",ask(l));
			del(l);
			del(r);
		}
	}
	return 0;
}

/*
5
1 95
1 54
1 51
2
3


*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值