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每行输出一个数,表示对应答案
Sample Input
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
Sample Output
106465
84185
492737
Hint
【数据规模与约定】1≤n≤10^5,−10^7≤x≤10^7
Treap的板子题啊,treap的常数比splay小,跑得比splay块,treap很好写,splay很难调,所以我选择写splay= =
首先先让我们明确treap和splay的作用:
旋转treap与splay左右相同,但LCT会用到splay,所以splay比旋转treap范围更广。
但是非旋转treap能够实现可持久化,这是splay做不到的。因此建议大家酌情考虑。
然而非旋转treap看得我一脸蒙蔽。。。像最开始看splay一样= =估计还是需要时间的沉淀吧~
这里再提一下splay的删除操作,如下图:
我们找到x的前驱,然后断开左右儿子,这时,再将前驱伸展到根(显然只有左树在伸展),然后我们把x的右儿子接到前驱上(前驱没有右儿子),就完成了删除操作。
#include<bits/stdc++.h>
using namespace std;
#define Inc(i,L,R) for(register int i=(L);i<=(R);++i)
#define Red(j,R,L) for(register int j=(R);j>=(L);--j)
const int N = 1e5+10;
struct Splay{
int rt,cur,p[N],ch[N][2];
int k[N],siz[N],cnt[N];
#define Ls(v) ch[v][0]
#define rs(v) ch[v][1]
#define sum(v) siz[v]=siz[Ls(v)]+siz[rs(v)]+cnt[v]
inline void rot(int x){
int f=p[x],gf=p[f],type=ch[f][1]==x,son=ch[x][!type];
ch[p[son]=f][type]=son,sum(f);
ch[p[f]=x][!type]=f,sum(x);
ch[p[x]=gf][ch[gf][1]==f]=x;
//p[ch[gf][ch[gf][1]==f]=p[ch[x][!type]=p[ch[f][type]=son]=f]=x]=gf;//压行选手了解一下?
}
inline void splay(int x){//关于splay单双旋。。。请上网查询或看我的另一篇blog
while(p[x]){
if(p[p[x]]&&((ch[p[p[x]]][1]==p[x])==(ch[p[x]][1]==x)))rot(p[x]);//x和父亲以及父亲的父亲在一条链上
rot(x);
}
rt=x;
}
inline void insert(int &x,int fa,int v){//注意一下重复节点的处理方法
if(k[x]==v)return ++siz[x],++cnt[x],splay(x),void();
if(!x)return siz[x=++cur]=cnt[x]=1,p[x]=fa,k[x]=v,splay(x),void();
insert(ch[x][k[x]<v],x,v);
}
inline int FindPre(int x){
x=Ls(x);
while(rs(x))x=rs(x);
return x;
}
inline int FindSub(int x){
x=rs(x);
while(Ls(x))x=Ls(x);
return x;
}
inline void Delete(int x,int v){
if(k[x]==v){
if(cnt[x]>1)return --siz[x],--cnt[x],splay(x),void();
splay(x);
if(Ls(x)){//有左儿子
int tmp=rs(x),New=FindPre(x);
splay(New);
rs(p[tmp]=New)=tmp;
sum(New);
}else p[rs(x)]=0,rt=rs(x);//没有左儿子注意特判
return ;
}
Delete(ch[x][k[x]<v],v);
}
inline void NumberRank(int v){
int x=rt,rank=0;
while(x){
if(k[x]==v)break;
if(k[x]<v)rank+=siz[Ls(x)]+cnt[x],x=rs(x);
else x=Ls(x);
}
cout<<rank+siz[Ls(x)]+1<<"\n";
splay(x);
}
inline void KthNumber(int kth){
int x=rt;
while(x){
if(siz[Ls(x)]+1<=kth&&kth<=siz[Ls(x)]+cnt[x])break;
if(kth>siz[Ls(x)]+cnt[x])kth-=siz[Ls(x)]+cnt[x],x=rs(x);
else x=Ls(x);
}
cout<<k[x]<<"\n";
splay(x);
}
inline void PreSub(int x,bool op){//这是极其暴力的,我们要查询一个数的前驱/后继,就先加进splay再删除= =
insert(rt,0,x);
if(op==0)cout<<k[FindPre(rt)]<<"\n";
if(op==1)cout<<k[FindSub(rt)]<<"\n";
Delete(rt,x);
}
}sp;
int main(){
int n;scanf("%d",&n);
while(n--){
int op,x;scanf("%d%d",&op,&x);
if(op==1)sp.insert(sp.rt,0,x);
if(op==2)sp.Delete(sp.rt,x);
if(op==3)sp.NumberRank(x);
if(op==4)sp.KthNumber(x);
if(op==5)sp.PreSub(x,0);
if(op==6)sp.PreSub(x,1);
}
return 0;
}
另外treap虽然写过,但是没什么大用,不过还是给出来,顺便提一下treap:
首先它是一个BST,然后它有一个“期望值”。
除了满足BST的性质,我们还要求它的每个节点的“期望值”满足小(大)根堆的性质,即treap是BST+堆,由于“期望值是随机的”,因此能保证log2n的时间复杂度,十分好写= =
#include<bits/stdc++.h>
using namespace std;
#define Inc(i,L,r) for(register int i=(L);i<=(r);++i)
#define Red(i,r,L) for(register int i=(r);i>=(L);--i)
const int N = 1e5+10;
struct Treap{
int rt,cur,k[N],siz[N],cnt[N],rank[N],c[N][2];
#define Ls c[x][0]
#define rs c[x][1]
#define sum(x) siz[x]=siz[Ls]+siz[rs]+cnt[x]
inline void rot(int &x,bool type){
int y=c[x][type];
c[x][type]=c[y][!type],c[y][!type]=x;
sum(x),sum(x=y);
}
inline void insert(int &x,int v){
if(!x){
rank[x=++cur]=rand()%N;
siz[x]=cnt[x]=1;
k[x]=v;
return ;
}
if(k[x]==v)return ++siz[x],++cnt[x],void();
bool type=k[x]<v;
insert(c[x][type],v);
sum(x);
if(rank[c[x][type]]<rank[x])rot(x,type);
}
inline bool Delete(int &x,int v){
if(!x)return 0;
if(k[x]==v){
if(cnt[x]>1)return --siz[x],--cnt[x],1;
if(!Ls||!rs)return x=Ls?Ls:rs,1;
rot(x,rank[Ls]<rank[rs]?0:1);
if(!Delete(x,v))return 0;
return sum(x),1;
}
bool type=k[x]<v;
if(!Delete(c[x][type],v))return 0;
return sum(x),1;
}
inline void FindNumber_Kth(int v){
int x=rt,rank=0;
while(k[x]^v){
if(k[x]<v)rank+=siz[Ls]+cnt[x],x=rs;
else x=Ls;
}
cout<<rank+siz[Ls]+1<<"\n";
}
inline void FindKth_Number(int kth){
int x=rt;
while(x){
if(siz[Ls]+1<=kth&&kth<=siz[Ls]+cnt[x])break;
if(siz[Ls]<kth)kth-=siz[Ls]+cnt[x],x=rs;
else x=Ls;
}
cout<<k[x]<<"\n";
}
inline void FindPre(int v){
int x=rt,Pre;
while(x){
if(k[x]<v)Pre=k[x],x=rs;
else x=Ls;
}
cout<<Pre<<"\n";
}
inline void FindNxt(int v){
int x=rt,Nxt;
while(x){
if(k[x]>v)Nxt=k[x],x=Ls;
else x=rs;
}
cout<<Nxt<<"\n";
}
}tp;
int main(){
srand(time(0));
int n;scanf("%d",&n);
while(n--){
int op,x;scanf("%d%d",&op,&x);
if(op==1)tp.insert(tp.rt,x);
if(op==2)tp.Delete(tp.rt,x);
if(op==3)tp.FindNumber_Kth(x);
if(op==4)tp.FindKth_Number(x);
if(op==5)tp.FindPre(x);
if(op==6)tp.FindNxt(x);
}
return 0;
}