@zzzc18
2017-12-28T09:08:22.000000Z
字数 3173
阅读 2287
可持久化数据结构 Treap
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)可以新建出一个与 状态相同的节点,并对这个新的节点进行修改,作为当前的版本。
这样一来,访问第 个版本对应的就是以 为根的平衡树。
时间复杂度 ,空间复杂度同样是
#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,不再+1rt[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));elseprintf("%d\n",succ(v,i,x));}return 0;}