可持久化数据结构算法总结
1.$CDQ$ 分治总结2.基环树算法总结3.(抄自己luogu上的博客)莫队总结4.珂朵莉树总结5.虚树总结6.从龟速乘到 $Miller-Rabin$ 算法(数论算法总结)7.浅谈李超线段树8.浅谈长链剖分9.浅谈平衡树
10.可持久化数据结构算法总结
11.线段树运用进阶12.群论学习笔记13.线性基学习笔记14.LCT 学习笔记15.原根学习笔记+BSGS复习笔记16.多项式算法初探:从 FFT 到 NTT17.下降幂、斯特林数学习笔记18.多项式算法再探:FMT 和 FWT19.min-max 容斥(最值反演)学习笔记20.自适应 Simpson 积分法学习笔记21.SAM 学习笔记22.min_25 筛 学习笔记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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)