题目
题目链接:https://www.luogu.com.cn/problem/P3369
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
- 插入 (x) 数
- 删除 (x) 数(若有多个相同的数,因只删除一个)
- 查询 (x) 数的排名(排名定义为比当前数小的数的个数 (+1) )
- 查询排名为 (x) 的数
- 求 (x) 的前驱(前驱定义为小于 (x),且最大的数)
- 求 (x) 的后继(后继定义为大于 (x),且最小的数)
(nleq 10^5,|x|leq 10^7)。
思路
Splay Treap
FHQ-Treap。
推荐 Blog
两个核心操作:split 和 merge。
split 把一棵 Treap 分成两棵,一般有按照前 (k) 个分和按照权值分。
merge 把两棵 Treap 合并成一棵。
通过这两个操作就可以完成平衡树各种其他操作。具体见代码。
时间复杂度 (O(nlog n))。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=100010;
int n,rt;
struct FHQ
{
int tot,ch[N][2],val[N],siz[N],dat[N];
void pushup(int x)
{
siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
}
int New(int v)
{
int x=++tot;
val[x]=v; siz[x]=1; dat[x]=rand();
return x;
}
void split(int x,int k,int &lc,int &rc)
{
if (!x) { lc=rc=0; return; }
if (val[x]<=k)
lc=x,split(ch[x][1],k,ch[x][1],rc);
else
rc=x,split(ch[x][0],k,lc,ch[x][0]);
pushup(x);
}
int merge(int x,int y)
{
if (!x || !y) return x|y;
if (dat[x]>dat[y])
{
ch[x][1]=merge(ch[x][1],y);
pushup(x);
return x;
}
else
{
ch[y][0]=merge(x,ch[y][0]);
pushup(y);
return y;
}
}
void ins(int v)
{
int x,y;
split(rt,v,x,y);
rt=merge(merge(x,New(v)),y);
}
void del(int v)
{
int x,y,z;
split(rt,v,x,y); split(x,v-1,x,z);
z=merge(ch[z][0],ch[z][1]);
rt=merge(merge(x,z),y);
}
int getrk(int v)
{
int x,y,z;
split(rt,v-1,x,y);
z=siz[x];
rt=merge(x,y);
return z+1;
}
int getval(int x,int k)
{
if (siz[ch[x][0]]==k-1) return val[x];
if (siz[ch[x][0]]>=k) return getval(ch[x][0],k);
else return getval(ch[x][1],k-siz[ch[x][0]]-1);
}
int pre(int v)
{
int x,y,z;
split(rt,v-1,x,y);
z=getval(x,siz[x]);
rt=merge(x,y);
return z;
}
int nxt(int v)
{
int x,y,z;
split(rt,v,x,y);
z=getval(y,1);
rt=merge(x,y);
return z;
}
}fhq;
int main()
{
srand(171023);
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
int opt,x;
scanf("%d%d",&opt,&x);
if (opt==1) fhq.ins(x);
if (opt==2) fhq.del(x);
if (opt==3) printf("%d
",fhq.getrk(x));
if (opt==4) printf("%d
",fhq.getval(rt,x));
if (opt==5) printf("%d
",fhq.pre(x));
if (opt==6) printf("%d
",fhq.nxt(x));
}
return 0;
}