bzoj 3224: 普通平衡树 简单splay版

Description

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)

Input

第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)

Output

对于操作3,4,5,6每行输出一个数,表示对应答案


        这两天重新练习splay,又参考了一众大神的博客,将各个大神最好写的一个函数糅合在一起,就形成了自己感觉超好写的平衡树,今天早上试着去打我这个模板,20分钟打完,10分钟调完,感觉不错,所以就发一波模板好了。

        对于代码中几个细节的解释:

        1.insert的时候不在递归中pushup是因为在最后splay到根的时候,自底向上就pushup了,不需要再在递归中加入(其实是懒得写)。

        2.delete的时候flag是用来判断找的是前驱还是后继的,因为可能没有后继,对于该操作,我们的做法是先将该点找到转到根部,找到它的如果有后继就将后继转到根部,这样现在的根也就是后继的右儿子就是要删除的那个点了,且它是没有左儿子的,直接连接上去即可。如果没有后继,则使用前驱也同理。

        3.找前驱和后继的时候没有插入当前值,而是通过一种类似贪心的方法来找的,以后继为例,如果当前点的值已经小于等于我要找的值了,那么向右走肯定答案更优,且如果我当前走到的点已经比我要找的值大了,就先将这个点的值作为答案,向左儿子的方向走,因为右儿子的值肯定比当前的值还大,所以就不用走了,这样就能找到前驱与后继了。

        p.s 我的做法是对于每插入一个数字,新开一个点,而不是如果这个数之前已经存在了,就给它的cnt++,这样虽然在数字全部一样的情况下复杂度仍为logn,不过复杂度还是比较优越的,bzoj亲测比机房写同样数字合并的还快。

       下附AC(今天早上30分钟打的)代码

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define maxn 100005
#define lson son[now][0]
#define rson son[now][1]
using namespace std;
int n,root,tot;
int son[maxn][2],fa[maxn],val[maxn],siz[maxn];
void pushup(int now)
{
	siz[now]=siz[lson]+siz[rson]+1;
}
void rotate(int x,int &k)
{
	int y=fa[x],z=fa[y],l,r;
	l=son[y][1]==x;r=l^1;
	if(y==k) k=x;
	else {if(son[z][0]==y) son[z][0]=x;else son[z][1]=x;}
	fa[x]=z;fa[y]=x;fa[son[x][r]]=y;
	son[y][l]=son[x][r];son[x][r]=y;
	pushup(y);pushup(x);
}
void splay(int x,int &k)
{
	while(x!=k)
	{
		int y=fa[x],z=fa[y];
		if(y!=k)
		{
			if((son[y][0]==x)^(son[z][0]==y)) rotate(x,k);
			else rotate(y,k);
		}
		rotate(x,k);
	}
}
void insert(int &now,int v,int last)
{
	if(!now){now=++tot;val[tot]=v;fa[now]=last;splay(tot,root);return;}
	if(v<=val[now]) insert(lson,v,now);
	else insert(rson,v,now);
}
void del(int v)
{
	int pos=root;
	while(val[pos]!=v) pos=son[pos][val[pos]<v]; splay(pos,root);
	
	int now=son[pos][1],flag;
	if(!now){flag=1;now=son[pos][0];}else flag=0;
	while(son[now][flag]) now=son[now][flag];
	
	int t=root;splay(now,root);
	son[now][flag]=son[t][flag];fa[son[t][flag]]=now;fa[now]=0;
	pushup(now);
}
int getrank(int v)
{
	int now=root,ans=0;
	while(now)
	{
		if(v>val[now]) {ans+=siz[lson]+1;now=rson;}
		else now=lson;
	}
	return ans+1;
}
int find(int v)
{
	int now=root;
	while(now)
	{
		if(siz[lson]+1==v) return val[now];
		else if(siz[lson]+1>v) now=lson;
		else v-=(siz[lson]+1),now=rson;
	}
	return val[now];
}
int suf(int v)
{
	int now=root,ans;
	while(now)
	{
		if(val[now]<v){ans=val[now];now=rson;}
		else now=lson;
	}
	return ans;
}
int nex(int v)
{
	int now=root,ans;
	while(now)
	{
		if(val[now]<=v) now=rson;
		else {ans=val[now];now=lson;}
	}
	return ans;
}
int main()
{
	scanf("%d",&n);
	while(n--)
	{
		int op,x;
		scanf("%d%d",&op,&x);
		if(op==1) insert(root,x,0);
		if(op==2) del(x);
		if(op==3) printf("%d\n",getrank(x));
		if(op==4) printf("%d\n",find(x));
		if(op==5) printf("%d\n",suf(x));
		if(op==6) printf("%d\n",nex(x));
	}
}


       



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值