可持久化数据结构算法总结

1.什么是可持久化

对应的具体问题就是给你一个可以用普通数据结构(如线段树、平衡树等)解决的问题,但是操作可能会在历史版本上进行。

由于主席树笔者曾经写过,所以这里跳过。

2.可持久化平衡树

思想很简单。众所周知,\(FHQ-Treap\) 是个好东西,他就可以用来写可持久化平衡树。

考虑 \(spilt\)\(merge\),在改变原先节点时,先新建一个和他完全一样的节点,再进行操作,这样就不会影响原先版本。

时间复杂度不变,为 \(O(n\log n)\)。下给出两道模板题代码:

//可持久化平衡树
#include<bits/stdc++.h>
#define ls(x) pl[x].ls
#define rs(x) pl[x].rs
#define val(x) pl[x].val
#define rk(x) pl[x].rk
#define sz(x) pl[x].sz
using namespace std;
const int inf=(1<<31)-1;
const int N=3e7+5;
struct fhq{
	int ls,rs,val,rk,sz;
}pl[N];int n,rt[N],id;
int mk(int x){
	pl[++id]={0,0,x,rand(),1};
	return id;
}void push_up(int x){
	sz(x)=sz(ls(x))+sz(rs(x))+1;
}inline void spilt(int x,int v,int &a,int &b){
	if(!x) return a=b=0,void();
	if(val(x)>v){
		b=mk(0),pl[b]=pl[x];
		spilt(ls(x),v,a,ls(b));
		push_up(b);
	}else{
		a=mk(0),pl[a]=pl[x];
		spilt(rs(x),v,rs(a),b);
		push_up(a);
	}
}inline int merge(int x,int y){
	if(!x&&!y) return 0;
	if(!x||!y){
		int re=mk(0);
		pl[re]=pl[x+y];
		return re;
	}if(rk(x)<rk(y)){
		int re=mk(0);pl[re]=pl[x];
		rs(re)=merge(rs(re),y);
		push_up(re);return re;
	}int re=mk(0);pl[re]=pl[y];
	ls(re)=merge(x,ls(re));
	push_up(re);return re;
}inline int rnk(int x,int v){
	if(!x) return inf;
	if(sz(ls(x))>=v) return rnk(ls(x),v);
	if(sz(ls(x))+1==v) return val(x);
	return rnk(rs(x),v-sz(ls(x))-1);
}int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	srand(time(0)),cin>>n;
	int v,opt,x,a,b,c;
	for(int i=1;i<=n;i++){
		cin>>v>>opt>>x;
		if(opt==1){
			spilt(rt[v],x-1,a,b);
			rt[i]=merge(a,merge(mk(x),b));
		}if(opt==2){
			spilt(rt[v],x-1,a,b),spilt(b,x,b,c);
			rt[i]=merge(a,merge(merge(ls(b),rs(b)),c));
		}if(opt==3){
			spilt(rt[v],x-1,a,b);
			cout<<sz(a)+1<<"\n";
			rt[i]=merge(a,b);
		}if(opt==4) cout<<rnk(rt[i]=rt[v],x)<<"\n";
		if(opt==5){
			spilt(rt[v],x-1,a,b);
			if(!a) cout<<-inf<<"\n";
			else cout<<rnk(a,sz(a))<<"\n";
			rt[i]=merge(a,b);
		}if(opt==6){
			spilt(rt[v],x,a,b);
			if(!b) cout<<inf<<"\n";
			cout<<rnk(b,1)<<"\n";
			rt[i]=merge(a,b);
		}
	}return 0;
}
//可持久化文艺平衡树
#include<bits/stdc++.h>
#define ll long long
#define ls(x) pl[x].ls
#define rs(x) pl[x].rs
#define rk(x) pl[x].rk
#define sz(x) pl[x].sz
#define fl(x) pl[x].fl
#define val(x) pl[x].val
#define sum(x) pl[x].sum
using namespace std;
const int N=3e7+5,M=2e5+5;
struct fhq{
	int ls,rs,rk,sz,fl,val;ll sum;
}pl[N];int n,rt[M],id;ll lst;
int mk(int x,int f){
	return pl[++id]={0,0,rand(),1,f,x,x},id;
}void push_up(int x){
	sz(x)=sz(ls(x))+sz(rs(x))+1;
	sum(x)=sum(ls(x))+sum(rs(x))+val(x); 
}void push_down(int x){
	if(!fl(x)) return;
	if(ls(x)) pl[++id]=pl[ls(x)],ls(x)=id;
	if(rs(x)) pl[++id]=pl[rs(x)],rs(x)=id;
	swap(ls(x),rs(x)),fl(x)=0;
	fl(ls(x))^=1,fl(rs(x))^=1;
}void spilt(int x,int v,int &a,int &b){
	if(!x) return a=b=0,void();
	push_down(x);
	if(sz(ls(x))<v){
		a=++id,pl[a]=pl[x];
		spilt(rs(x),v-sz(ls(x))-1,rs(a),b);
		push_up(a);
	}else{
		b=++id,pl[b]=pl[x];
		spilt(ls(x),v,a,ls(b));
		push_up(b);
	}
}int merge(int x,int y){
	if(!x&&!y) return 0;
	if(!x||!y){
		pl[++id]=pl[x+y];
		return id;
	}push_down(x);
	push_down(y);
	if(rk(x)<rk(y)){
		int re=++id;pl[re]=pl[x];
		rs(re)=merge(rs(re),y);
		return push_up(re),re;
	}int re=++id;pl[re]=pl[y];
	ls(re)=merge(x,ls(re));
	return push_up(re),re;
}int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	srand(time(0)),cin>>n;
	ll v,opt,p,x,l,r;int a,b,c;
	for(int i=1;i<=n;i++){
		cin>>v>>opt;
		if(opt==1){
			cin>>p>>x,p^=lst,x^=lst;
			spilt(rt[v],p,a,b);
			rt[i]=merge(merge(a,mk(x,0)),b);
		}if(opt==2){
			cin>>p,p^=lst;
			spilt(rt[v],p,b,c),spilt(b,p-1,a,b);
			rt[i]=merge(merge(a,merge(ls(b),rs(b))),c);
		}if(opt==3){
			cin>>l>>r,l^=lst,r^=lst;
			spilt(rt[v],r,b,c),spilt(b,l-1,a,b);
			pl[++id]=pl[b],fl(b=id)^=1;
			rt[i]=merge(merge(a,b),c);
		}if(opt==4){
			cin>>l>>r,l^=lst,r^=lst;
			spilt(rt[v],r,b,c),spilt(b,l-1,a,b);
			cout<<(lst=sum(b))<<"\n";
			rt[i]=merge(merge(a,b),c);
		}
	}return 0;
}

3.可持久化 \(Trie\)

和主席树不能说思想相近,只能说一模一样。直接新增节点即可。

下面这道题的思路是对于可能的次大值区间进行询问。

//HEOI2013 ALO
#include<bits/stdc++.h>
using namespace std;
const int N=50005,M=3e6+5;
int n,rt[N],ch[M][2],st[N][20],ln[N],tot,as;
void Sparse_Table(){
	for(int j=0;j<19;j++)
		for(int i=1;i<=n-(1<<(j+1))+1;i++)
			st[i][j+1]=max(st[i][j],st[i+(1<<j)][j]);
}int rmq(int l,int r){
	int k=ln[r-l+1],x=r-(1<<k)+1;
	return max(st[l][k],st[x][k]);
}void add(int &x,int y,int v,int id){
	if(!x) x=++tot;
	if(id<0) return;
	int c=((v>>id)&1);
	ch[x][c^1]=ch[y][c^1];
	add(ch[x][c],ch[y][c],v,id-1);
}int ans(int x,int y,int v,int id){
	if(id<0) return 0;
	int c=1-((v>>id)&1);
	if(ch[x][c]==ch[y][c])
		return ans(ch[x][c^1],ch[y][c^1],v,id-1);
	return (1<<id)+ans(ch[x][c],ch[y][c],v,id-1);
}int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n,ln[0]=-1;
	for(int i=1,x;i<=n;i++){
		ln[i]=ln[i/2]+1,cin>>st[i][0];
		add(rt[i],rt[i-1],st[i][0],30);
	}Sparse_Table();
	for(int i=1;i<=n;i++){
		int lx=0,ly,rx,ry,l=1,r=i-1,re=0;
		while(l<=r){
			int mid=(l+r)/2;
			if(rmq(mid,i)>st[i][0])
				lx=mid,l=mid+1;
			else r=mid-1;
		}ly=(lx==0)?-1:0,l=1,r=lx-1;
		while(l<=r){
			int mid=(l+r)/2;
			if(rmq(mid,lx-1)>st[i][0])
				ly=mid,l=mid+1;
			else r=mid-1;
		}rx=n+1,l=i+1,r=n;
		while(l<=r){
			int mid=(l+r)/2;
			if(rmq(i,mid)>st[i][0])
				rx=mid,r=mid-1;
			else l=mid+1;
		}ry=n+(rx==n+1?2:1),l=rx+1,r=n;
		while(l<=r){
			int mid=(l+r)/2;
			if(rmq(rx+1,mid)>st[i][0])
				ry=mid,r=mid-1;
			else l=mid+1;
		}if(ry-2<n) re=ans(rt[lx],rt[ry-1],st[i][0],30);
		if(~ly) re=max(re,ans(rt[ly],rt[rx-1],st[i][0],30));
		as=max(as,re);
	}return cout<<as,0;
}
posted @   长安一片月_22  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示