Kirin:可持久化数据结构可以保留历史版本,用新建代替修改。
题目描述
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作(对于各个以往的历史版本):
1.插入x数
2.删除x数(若有多个相同的数,因只删除一个,如果没有请忽略该操作)
3.查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名)
4.查询排名为x的数
5.求x的前驱(前驱定义为小于x,且最大的数,如不存在输出-2147483647)
6.求x的后继(后继定义为大于x,且最小的数,如不存在输出2147483647)
和原本平衡树不同的一点是,每一次的任何操作都是基于某一个历史版本,同时生成一个新的版本。(操作3, 4, 5, 6即保持原版本无变化)
每个版本的编号即为操作的序号(版本0即为初始状态,空树)
这里使用FHQ_Treap
,因为这种平衡树不进行旋转,并且便于可持久化的理解
其实如果有主席树
的基础,理解这个应该不难。
每次操作都要记录一个单独的根,真正有区别的的地方只在split()
int Copy_Node(int pre){
int now=New_Node();
tree[now]=tree[pre];
return now;
}
PAIR split(int k,int val){
PAIR re(0,0);
if(!k)
return re;
int now=Copy_Node(k);
if(val<tree[k].val){
re=split(tree[k].ls,val);
tree[now].ls=re.right;
re.right=now;
}
else{
re=split(tree[k].rs,val);
tree[now].rs=re.left;
re.left=now;
}
update(now);
return re;
}
Copy_Node(int pre)
可以新建出一个与 \(pre\) 状态相同的节点,并对这个新的节点进行修改,作为当前的版本。
这样一来,访问第 \(k\) 个版本对应的就是以 \(rt[k]\) 为根的平衡树。
时间复杂度 \(O(nlogn)\),空间复杂度同样是 \(O(nlogn)\)
#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 500000+9;
int n;
struct T{
int ls,rs;
int RD;
int size,val;
}tree[22000576];
int rt[MAXN];
struct PAIR{
int left,right;
PAIR(int _x=0,int _y=0):left(_x),right(_y){}
};
int New_Node(int val=0){
static int cnt;
cnt++;
tree[cnt].val=val;
tree[cnt].RD=rand();
tree[cnt].size=1;
return cnt;
}
int Copy_Node(int pre){
int now=New_Node();
tree[now]=tree[pre];
return now;
}
void update(int x){
tree[x].size=tree[tree[x].ls].size+tree[tree[x].rs].size+1;
}
PAIR split(int k,int val){
PAIR re(0,0);
if(!k)
return re;
int now=Copy_Node(k);
if(val<tree[k].val){
re=split(tree[k].ls,val);
tree[now].ls=re.right;
re.right=now;
}
else{
re=split(tree[k].rs,val);
tree[now].rs=re.left;
re.left=now;
}
update(now);
return re;
}
int merge(int x,int y){
if(x==0 || y==0)
return x+y;
if(tree[x].RD<tree[y].RD){
tree[x].rs=merge(tree[x].rs,y);
update(x);
return x;
}
else{
tree[y].ls=merge(x,tree[y].ls);
update(y);
return y;
}
}
void insert(int pre,int now,int val){
PAIR rt1=split(rt[pre],val);
rt[now]=merge(rt1.left,merge(New_Node(val),rt1.right));
}
void del(int pre,int now,int val){
PAIR rt1=split(rt[pre],val-1);
PAIR rt2=split(rt1.right,val);
rt2.left=merge(tree[rt2.left].ls,tree[rt2.left].rs);
rt[now]=merge(rt1.left,merge(rt2.left,rt2.right));
}
int n2r(int pre,int now,int val){
PAIR rt1=split(rt[pre],val-1);
int re=tree[rt1.left].size;//由于有-INF,不再+1
rt[now]=merge(rt1.left,rt1.right);
return re;
}
int r2n(int now,int val){
int k=now;
while(val<=tree[tree[k].ls].size || val>tree[tree[k].ls].size+1){
if(val<=tree[tree[k].ls].size)
k=tree[k].ls;
else{
val-=tree[tree[k].ls].size+1;
k=tree[k].rs;
}
}
return tree[k].val;
}
int r2n(int pre,int now,int val){
rt[now]=rt[pre];
return r2n(rt[now],val+1);
}
int pred(int pre,int now,int val){
PAIR rt1=split(rt[pre],val-1);
int re=r2n(rt1.left,tree[rt1.left].size);
rt[now]=merge(rt1.left,rt1.right);
return re;
}
int succ(int pre,int now,int val){
PAIR rt1=split(rt[pre],val);
int re=r2n(rt1.right,1);
rt[now]=merge(rt1.left,rt1.right);
return re;
}
int main(){
srand(time(0));
insert(0,0,-2147483647);
insert(0,0,2147483647);
scanf("%d",&n);
for(int i=1;i<=n;i++){
int v,opt,x;
scanf("%d%d%d",&v,&opt,&x);
if(opt==1)
insert(v,i,x);
else if(opt==2)
del(v,i,x);
else if(opt==3)
printf("%d\n",n2r(v,i,x));
else if(opt==4)
printf("%d\n",r2n(v,i,x));
else if(opt==5)
printf("%d\n",pred(v,i,x));
else
printf("%d\n",succ(v,i,x));
}
return 0;
}