【洛谷P3835】【模板】可持久化平衡树
题目
题目链接:https://www.luogu.com.cn/problem/P3835
您需要写一种数据结构(可参考题目标题),来维护一个可重整数集合,其中需要提供以下操作( 对于各个以往的历史版本 ):
- 插入 \(x\)
- 删除 \(x\)(若有多个相同的数,应只删除一个,如果没有请忽略该操作)
- 查询 \(x\) 的排名(排名定义为比当前数小的数的个数 \(+1\))
- 查询排名为 \(x\) 的数
- 求 \(x\) 的前驱(前驱定义为小于 \(x\),且最大的数,如不存在输出 \(-2^{31}+1\) )
- 求 \(x\) 的后继(后继定义为大于 \(x\),且最小的数,如不存在输出 \(2^{31}-1\) )
和原本平衡树不同的一点是,每一次的任何操作都是基于某一个历史版本,同时生成一个新的版本。(操作3, 4, 5, 6即保持原版本无变化)
每个版本的编号即为操作的序号(版本0即为初始状态,空树)
\(n\leq 5\times 10^5,|x|\leq 10^9\)。
思路
直接上 FHQ Treap 即可。注意每次 split 和 merge 时都要复制一份节点。
注意因为 FHQ Treap 是期望深度 \(\log n\) 的,所以节点数量不能开的太死,我开的是 \(50\) 倍。
时间复杂度 \(O(n\log n)\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=500010,MAXN=N*50,Inf=2147483647;
int n,rt[N];
struct FHQ
{
int tot,ch[MAXN][2],val[MAXN],dat[MAXN],siz[MAXN];
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; dat[x]=rand(); siz[x]=1;
return x;
}
int cpynode(int x)
{
int y=++tot;
ch[y][0]=ch[x][0]; ch[y][1]=ch[x][1];
val[y]=val[x]; dat[y]=dat[x]; siz[y]=siz[x];
return y;
}
void build()
{
int x=New(Inf),y=New(-Inf);
ch[x][0]=y; pushup(x);
rt[0]=x;
}
void split(int x,int k,int &lc,int &rc)
{
if (!x) { lc=rc=0; return; }
int y=cpynode(x);
if (val[y]<=k)
lc=y,split(ch[y][1],k,ch[y][1],rc);
else
rc=y,split(ch[y][0],k,lc,ch[y][0]);
pushup(y);
}
int merge(int x,int y)
{
if (!x || !y) return x|y;
if (dat[x]>dat[y])
{
int z=cpynode(x);
ch[z][1]=merge(ch[z][1],y);
pushup(z); return z;
}
else
{
int z=cpynode(y);
ch[z][0]=merge(x,ch[z][0]);
pushup(z); return z;
}
}
void ins(int &root,int v)
{
int x,y;
split(root,v,x,y);
root=merge(merge(x,New(v)),y);
}
void del(int &root,int v)
{
int x,y,z;
split(root,v,x,y); split(x,v-1,x,z);
if (val[z]==v) z=merge(ch[z][0],ch[z][1]);
root=merge(merge(x,z),y);
}
int getrk(int &root,int v)
{
int x,y,z;
split(root,v-1,x,y);
z=siz[x];
root=merge(x,y);
return z;
}
int getval(int x,int k)
{
if (siz[ch[x][0]]+1==k) 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 &root,int v)
{
int x,y,z;
split(root,v-1,x,y);
z=getval(x,siz[x]);
root=merge(x,y);
return z;
}
int nxt(int &root,int v)
{
int x,y,z;
split(root,v,x,y);
z=getval(y,1);
root=merge(x,y);
return z;
}
}fhq;
int main()
{
srand(1023);
scanf("%d",&n);
fhq.build();
for (int i=1;i<=n;i++)
{
int opt,now,x;
scanf("%d%d%d",&now,&opt,&x);
rt[i]=rt[now];
if (opt==1) fhq.ins(rt[i],x);
if (opt==2) fhq.del(rt[i],x);
if (opt==3) printf("%d\n",fhq.getrk(rt[i],x));
if (opt==4) printf("%d\n",fhq.getval(rt[i],x+1));
if (opt==5) printf("%d\n",fhq.pre(rt[i],x));
if (opt==6) printf("%d\n",fhq.nxt(rt[i],x));
}
return 0;
}