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
I think
代码参考(基本是二者的混合体qvq):
史上最详尽的平衡树(splay)讲解与模板
Splay伸展树学习笔记
splay板子……
稍微一提的是求前驱(后继),相当于新插入一个节点(顺便就旋至根节点啦),找根节点的左(右)子树中val最大(小)的节点,找到之后再删去该新建节点。
对于删除节点,可利用find()函数(查询x数的排名)将目标点旋至根节点,通过判断其子树情况分类处理。这里具体写写两颗子树均不为空的删除操作。
找到被删除节点x的左子树中val最大的节点y,并将其旋至x左子树根,由于y的val值大于左子树中任意其他节点,因此y一定没有右子树。那么将原x的右子树接至y的右子树,并将相关信息修改好就ok啦。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int sm = 1e5+5;
int u,n,m,sz,opt,rt;
int f[sm],c[sm],cnt[sm],size[sm],ch[sm][2];
char a;
void read(int &x) {
x=0;a=getchar();u=1;
while(a>'9'||a<'0') {
if(a=='-')u=-1;a=getchar();
}
while(a>='0'&&a<='9')x=x*10+a-'0',a=getchar();
x*=u;
}
bool son(int p,int fa) {//判断p是fa的左子树还是右子树
return ch[fa][1]==p;
}
void clear(int p) {//删除点p的各项信息
f[p]=ch[p][0]=ch[p][1]=size[p]=cnt[p]=c[p]=0;
}
void newnode(int val,int fa) {//给fa创建一个新的子节点,记得在调用之后更新fa的ch
++sz;f[sz]=fa;
ch[sz][0]=ch[sz][1]=0;
size[sz]=1;cnt[sz]=1;c[sz]=val;
}
void update(int x) {//更新以x为根的子树大小
if(x) {
size[x]=cnt[x];
if(ch[x][0])size[x]+=size[ch[x][0]];
if(ch[x][1])size[x]+=size[ch[x][1]];
}
}
void rotate(int x) {//把x旋至其父亲
int fa=f[x],gf=f[fa];
int a=son(x,fa),b=!a;
ch[fa][a]=ch[x][b];
if(ch[x][b])f[ch[x][b]]=fa;
ch[x][b]=fa,f[fa]=x;
f[x]=gf;
if(gf)ch[gf][son(fa,gf)]=x;
else rt=x;
update(fa);update(x);
}
void splay(int x,int p) {//将x旋至p的子节点
while(f[x]!=p) {
int fa=f[x],gf=f[fa];
if(p==gf)rotate(x);
else {
if(son(x,fa)^son(fa,gf))
rotate(x),rotate(x);
else rotate(fa),rotate(x);
}
}
}
void insert(int val) {//插入一个值为val的新节点
if(!rt){ newnode(val,0);rt=sz;return;}
int now=rt,fa=f[now];
while(now) {
if(c[now]==val) {
splay(now,0);cnt[now]++;return;//记得将节点旋至根
}
fa=now;
now=ch[fa][val>c[fa]];
if(!now) {
newnode(val,fa);
ch[fa][val>c[fa]]=sz;
update(fa);splay(sz,0);return;//记得将节点旋至根
}
}
}
int find(int val) {//查询值为val的点的排名
int ans=0,now=rt;
while(now) {
if(val<c[now])
now=ch[now][0];
else {
ans+=(ch[now][0]?size[ch[now][0]]:0);
if(val==c[now]) { splay(now,0);return ans+1;}
ans+=cnt[now];
now=ch[now][val>c[now]];
}
}
}
int findx(int p) {//查询排名为p的数
int ans=0,now=rt;
while(now) {
if(ch[now][0]&&p<=size[ch[now][0]])
now=ch[now][0];
else {
int tmp=(ch[now][0]?size[ch[now][0]]:0)+cnt[now];
if(p<=tmp)return c[now];
p-=tmp;
now=ch[now][1];
}
}
}
int pre() {//求前驱
int now=ch[rt][0];
while(ch[now][1])
now=ch[now][1];
return now;
}
int nxt() {//求后继
int now=ch[rt][1];
while(ch[now][0])
now=ch[now][0];
return now;
}
void del(int val) {//删除值为val的点
find(val);
if(cnt[rt]>1){--cnt[rt];return;}
if(!ch[rt][0]&&!ch[rt][1]){ clear(rt);return;}
else if(!ch[rt][0]) {
int t=rt;rt=ch[rt][1];f[rt]=0;clear(t);return;
}
else if(!ch[rt][1]) {
int t=rt;rt=ch[rt][0];f[rt]=0;clear(t);return;
}
int l=pre();
splay(l,rt);
f[ch[rt][1]]=l;
ch[l][1]=ch[rt][1];
f[l]=0;clear(rt);
rt=l;update(rt);
}
int main() {
read(n);
while(n--) {
read(opt);read(m);
if(opt==1)insert(m);
else if(opt==2)del(m);
else if(opt==3)printf("%d\n",find(m));
else if(opt==4)printf("%d\n",findx(m));
else {
insert(m);
if(opt==5)printf("%d\n",c[pre()]);
else printf("%d\n",c[nxt()]);
del(m);
}
}
return 0;
}