普通平衡树(Spaly)模版

题面

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

插入xx数
删除xx数(若有多个相同的数,因只删除一个)
查询xx数的排名(排名定义为比当前数小的数的个数+1+1。若有多个相同的数,因输出最小的排名)
查询排名为xx的数
求xx的前驱(前驱定义为小于xx,且最大的数)
求xx的后继(后继定义为大于xx,且最小的数)

代码

操作就不想口胡了,注意要先insert极大值和极小值。因为防止查前驱后继以及只存有一个数的时候的时候越界。同时注意查找第k大时因为k+1,因为insert了一个极小值。在敲的时候模拟一下就可以理解了

表示
ch[N][2]:ch[x][0]代表 xx 的左儿子,ch[x][1]代表 xx 的右儿子。
val[N]:val[x]代表 xx 存储的值。
cnt[N]:cnt[x]代表 xx 存储的重复权值的个数。
par[N]:par[x]代表 xx 的父节点。
size[N]:size[x]代表 xx 子树下的储存的权值数(包括重复权值)。

#include<bits/stdc++.h>
#define inf 1<<30 
using namespace std; 
const int maxn=2e5+10; 
int ch[maxn][2],par[maxn],val[maxn],cnt[maxn],size[maxn];
int ncnt,root;//ncnt新建结点位置,root 表示根节点 
int n;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
bool chk(int x)
{
	return ch[par[x]][1]==x;
}
void pushup(int x)
{
	size[x]=size[ch[x][0]]+size[ch[x][1]]+cnt[x];
}
void rotate(int x)
{
	int y=par[x],z=par[y],k=chk(x),w=ch[x][k^1];
	ch[y][k]=w;par[w]=y;
	ch[z][chk(y)]=x;par[x]=z;
	ch[x][k^1]=y;par[y]=x;
	pushup(y);pushup(x);
}
void splay(int x,int goal=0)
{
	while(par[x]!=goal)
	{
		int y=par[x],z=par[y];
		if(z!=goal)
		{   //两个结点位置相同
			if(chk(x)==chk(y))rotate(y);
			else rotate(x); 
		}
		rotate(x);
	}
	if(!goal)root=x;
}
void insert(int x)
{
	int cur=root,p=0;//p记录当前节点 
	while(cur&&val[cur]!=x)
	p=cur,cur=ch[cur][x>val[cur]];
	if(cur)cnt[cur]++;
	else
	{
		cur=++ncnt;
		if(p)ch[p][x>val[p]]=cur; 
		ch[cur][0]=ch[cur][1]=0; 
		par[cur]=p;val[cur]=x;
		cnt[cur]=size[cur]=1; 
	}
	splay(cur);
}
void find(int x)//把某点旋到根节点 
{
	int cur=root;
	while(ch[cur][x>val[cur]]&&x!=val[cur])
	cur=ch[cur][x>val[cur]];//找到该节点 
	splay(cur);
}
int kth(int k)
{
	int cur=root;
	while(1)
	{
		if(ch[cur][0]&&k<=size[ch[cur][0]])
		cur=ch[cur][0];
		else if(k>size[ch[cur][0]]+cnt[cur])
		k-=size[ch[cur][0]]+cnt[cur],cur=ch[cur][1];
		else return cur;
	}
}
int pre(int x)
{
	find(x);
	if(val[root]<x)return root;
	int cur=ch[root][0];
	while(ch[cur][1])cur=ch[cur][1];
	return cur;
}
int succ(int x)
{
	find(x);
	if(val[root]>x)return root;
	int cur=ch[root][1];
	while(ch[cur][0])cur=ch[cur][0];
	return cur;
}
void remove(int x)
{
	int last=pre(x),next=succ(x);
	splay(last);splay(next,last);
	int del=ch[next][0];//表示要删的点 
	if(cnt[del]>1)
	cnt[del]--,splay(del);//更新size标记 
	else ch[next][0]=0; 
}
int main()
{
	n=read();
    insert(inf);
    insert(-inf);
	for(int i=1;i<=n;i++)
	{
		int op=read(),x=read();
		if(op==1)insert(x);
		else if(op==2)remove(x);
		else if(op==3)find(x),printf("%d\n",size[ch[root][0]]);
		else if(op==4)printf("%d\n",val[kth(x+1)]);
		else if(op==5)printf("%d\n",val[pre(x)]);
		else if(op==6)printf("%d\n",val[succ(x)]);
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值