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));
}
}