可持久化平衡树

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;
}
posted @ 2018-01-21 13:01  zzzc18  阅读(328)  评论(0编辑  收藏  举报