1.可持久化線段樹(可持久化數組)
https://www.luogu.org/problemnew/show/P3919#sub
最基礎的可持久化數據結構,每次修改開新的log個點即可。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+100;
template<class T>
void rd(T &x)
{
char c=getchar();x=0;bool f=0;
while(!isdigit(c))f|=(c=='-'),c=getchar();
while(isdigit(c))x=x*10+c-48,c=getchar();
if(f)x=-x;
}
template<class T>
void print(T x)
{
static int tax[50],tnum;
tnum=0;
if(x<0)putchar('-'),x*=-1;
while(x)tax[++tnum]=x%10,x/=10;
for(int i=tnum;i>=1;i--)putchar('0'+tax[i]%10);
puts("");
}
struct Seg{
int ls,rs,w;
}seg[N*20];
int tot,edit[N];
int n,m,a[N],num=0;
int build(int l,int r)
{
int nw=++tot;
if(l==r)
{
seg[nw].w=a[l];
return nw;
}
int mid=(l+r)>>1;
seg[nw].ls=build(l,mid);
seg[nw].rs=build(mid+1,r);
return nw;
}
int cg(int bf,int to,int l,int r,int w)
{
int nw=++tot;
seg[nw]=seg[bf];
if(l==r)
{
seg[nw].w=w;
return nw;
}
int mid=(l+r)>>1;
if(to<=mid)seg[nw].ls=cg(seg[bf].ls,to,l,mid,w);
else seg[nw].rs=cg(seg[bf].rs,to,mid+1,r,w);
return nw;
}
int qry(int nw,int to,int l,int r)
{
if(l==r)return seg[nw].w;
int mid=(l+r)>>1;
if(to<=mid)return qry(seg[nw].ls,to,l,mid);
else return qry(seg[nw].rs,to,mid+1,r);
}
int main()
{
rd(n),rd(m);
for(int i=1;i<=n;i++)
rd(a[i]);
edit[0]=build(1,n);
int wh,op,ps,val;
while(m--)
{
rd(wh),rd(op);
if(op==1)
{
rd(ps),rd(val);
edit[++num]=cg(edit[wh],ps,1,n,val);
}
else
{
rd(ps);
edit[++num]=edit[wh];
print(qry(edit[wh],ps,1,n));
}
}
}
靜態區間第k小,很久以前寫的也貼在這了
#pragma GCC optimize(3,"inline","Ofast")
#include<bits/stdc++.h>
using namespace std;
const int N=2e5;
int a[N+5],b[N+5],ls[N*20],rs[N*20],rt[N+5],seg[N*20],tot=0;
void read(int &x)
{
char c=getchar();x=0;
while(!isdigit(c))c=getchar();
while(isdigit(c))x=(x<<3)+(x<<1)+c-48,c=getchar();
}
int build(int l,int r)
{
int nw=++tot,mid=(l+r)>>1;
if(l>=r)return nw;
ls[nw]=build(l,mid);
rs[nw]=build(mid+1,r);
return nw;
}
int add(int bf,int l,int r,int x)
{
int nw=++tot,mid=(l+r)>>1;
ls[nw]=ls[bf],rs[nw]=rs[bf],seg[nw]=seg[bf]+1;
if(l>=r)return nw;
if(x<=mid)ls[nw]=add(ls[bf],l,mid,x);
else rs[nw]=add(rs[bf],mid+1,r,x);
return nw;
}
int query(int a,int b,int l,int r,int k)
{
if(l==r)return l;
int nw=seg[ls[b]]-seg[ls[a]],mid=(l+r)>>1;
if(nw>=k)return query(ls[a],ls[b],l,mid,k);
else return query(rs[a],rs[b],mid+1,r,k-nw);
}
int main()
{
int n,m,len,aa,bb,k;
read(n),read(m);
for(int i=1;i<=n;i++)
read(a[i]),b[i]=a[i];
sort(b+1,b+n+1);
len=unique(b+1,b+n+1)-b-1;
rt[0]=build(1,len);
for(int i=1;i<=n;i++){
a[i]=lower_bound(b+1,b+len+1,a[i])-b;
rt[i]=add(rt[i-1],1,len,a[i]);
}
for(int i=1;i<=m;i++){
read(aa),read(bb),read(k);
printf("%d\n",b[query(rt[aa-1],rt[bb],1,len,k)]);
}
return 0;
}
2.可持久化平衡樹(fhq treap)
模板題
鏈接:https://www.luogu.org/problemnew/show/P3835
基本和之前線段樹一樣,mg和split發生改變的點都開成新點即可。個人覺得爲了減少空間消耗可以分別寫開新點和不開新點的mg/split,但懶得寫......
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+100;
const int inf=INT_MAX;
template<class T>
void rd(T &x)
{
char c=getchar();x=0;bool f=0;
while(!isdigit(c))f|=(c=='-'),c=getchar();
while(isdigit(c))x=x*10+c-48,c=getchar();
if(f)x=-x;
}
struct gg{
int rnd,ls,rs,sz,w;
}nd[N*60];
int n,edit[N],tot=0;
void push_up(int x)
{nd[x].sz=nd[nd[x].ls].sz+nd[nd[x].rs].sz+1;}
int mg(int x,int y)
{
if(!x||!y)return x+y;
int nw=++tot;
if(nd[x].rnd<=nd[y].rnd)
{
nd[nw]=nd[x];
nd[nw].rs=mg(nd[nw].rs,y);
push_up(nw);
return nw;
}
else
{
nd[nw]=nd[y];
nd[nw].ls=mg(x,nd[nw].ls);
push_up(nw);
return nw;
}
}
void split1(int nw,int k,int &x,int &y)
{
if(!nw)x=y=0;
else
{
int p=++tot;
nd[p]=nd[nw];
if(nd[nd[p].ls].sz>=k)y=p,split1(nd[p].ls,k,x,nd[p].ls);
else x=p,split1(nd[p].rs,k-nd[nd[p].ls].sz-1,nd[p].rs,y);
push_up(p);
}
}
void split2(int nw,int k,int &x,int &y)
{
if(!nw)x=y=0;
else
{
int p=++tot;
nd[p]=nd[nw];
if(nd[p].w<=k)x=p,split2(nd[p].rs,k,nd[p].rs,y);
else y=p,split2(nd[p].ls,k,x,nd[p].ls);
push_up(p);
}
}
void P(int x)
{
if(nd[x].ls)P(nd[x].ls);
printf("%d ",nd[x].w);
if(nd[x].rs)P(nd[x].rs);
}
int new_node(int x)
{
int nw=++tot;
nd[nw].w=x,nd[nw].sz=1,nd[nw].ls=nd[nw].rs=0;
nd[nw].rnd=rand();
return nw;
}
void ins(int val,int id,int nw)
{
int x,y;
split2(edit[id],val,x,y);
edit[nw]=mg(mg(x,new_node(val)),y);
}
void del(int val,int id,int nw)
{
int x,y,z;
split2(edit[id],val-1,x,y);
split1(y,1,y,z);
if(nd[y].w!=val)edit[nw]=edit[id];
else edit[nw]=mg(x,z);
}
void rnk(int val,int id,int nw)
{
int x,y;
edit[nw]=edit[id];
split2(edit[id],val-1,x,y);
printf("%d\n",nd[x].sz);
}
void kth(int k,int id,int nw)
{
int x,y,z;
edit[nw]=edit[id];
split1(edit[id],k-1,x,y);
split1(y,1,y,z);
printf("%d\n",nd[y].w);
}
void pre(int val,int id,int nw)
{
int x,y,z;
edit[nw]=edit[id];
split2(edit[id],val-1,x,y);
split1(x,nd[x].sz-1,x,z);
printf("%d\n",nd[z].w);
}
void nxt(int val,int id,int nw)
{
int x,y,z;
edit[nw]=edit[id];
split2(edit[id],val,x,y);
split1(y,1,y,z);
printf("%d\n",nd[y].w);
}
int main()
{
rd(n);
ins(inf,0,0),ins(-inf,0,0);
int id,op,x;
for(int i=1;i<=n;i++)
{
rd(id),rd(op),rd(x);
if(op==1)ins(x,id,i);
if(op==2)del(x,id,i);
if(op==3)rnk(x,id,i);
if(op==4)kth(x+1,id,i);
if(op==5)pre(x,id,i);
if(op==6)nxt(x,id,i);
}
}
可持久化文藝平衡樹
翻轉也是一樣的,注意每次push_down也要開新點,不然會影響歷史版本,而這裏按我之前那種較標準的翻轉寫法(見LCT篇)會比較翔而且空間常數可能還要×2,因此這裏用了最簡單的翻轉寫法(即當前點有翻轉標記但是其實並未翻轉)
ps:這種簡單的翻轉寫法可能在一些題裏要多push_down幾次保證當前點的左右孩子順序是實際的順序,原來的寫法則不用,因此個人在沒有特殊要求下更傾向於寫原來的那種
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+100;
const int M=3e7+100;
template<class T>
void rd(T &x)
{
char c=getchar();x=0;bool f=0;
while(!isdigit(c))f|=(c=='-'),c=getchar();
while(isdigit(c))x=x*10+c-48,c=getchar();
if(f)x=-x;
}
struct gg{
int rnd,ls,rs,sz,w;
bool rev;ll sum;
}nd[M];
int n,edit[N],tot=0;ll las_ans=0;
void push_up(int x)
{
nd[x].sz=nd[nd[x].ls].sz+nd[nd[x].rs].sz+1;
nd[x].sum=nd[nd[x].ls].sum+nd[nd[x].rs].sum+nd[x].w;
}
void push_down(int x)
{
if(nd[x].rev)
{
int nw;
if(nd[x].ls)nw=++tot,nd[nw]=nd[nd[x].ls],nd[x].ls=nw;
if(nd[x].rs)nw=++tot,nd[nw]=nd[nd[x].rs],nd[x].rs=nw;
swap(nd[x].ls,nd[x].rs);
if(nd[x].ls)nd[nd[x].ls].rev^=1;
if(nd[x].rs)nd[nd[x].rs].rev^=1;
nd[x].rev=0;
}
}
int mg(int x,int y)
{
if(!x||!y)return x+y;
push_down(x),push_down(y);
int nw=++tot;
if(nd[x].rnd<=nd[y].rnd)
{
nd[nw]=nd[x];
nd[nw].rs=mg(nd[nw].rs,y);
push_up(nw);
return nw;
}
else
{
nd[nw]=nd[y];
nd[nw].ls=mg(x,nd[nw].ls);
push_up(nw);
return nw;
}
}
void split1(int nw,int k,int &x,int &y)
{
if(!nw)x=y=0;
else
{
int p=++tot;
push_down(nw);
nd[p]=nd[nw];
if(nd[nd[p].ls].sz>=k)y=p,split1(nd[p].ls,k,x,nd[p].ls);
else x=p,split1(nd[p].rs,k-nd[nd[p].ls].sz-1,nd[p].rs,y);
push_up(p);
}
}
int new_node(int x)
{
int nw=++tot;
nd[nw].w=x,nd[nw].sz=1,nd[nw].ls=nd[nw].rs=0;
nd[nw].rnd=rand(),nd[nw].sum=x,nd[nw].rev=0;
return nw;
}
void ins(int ps,int val,int id,int nw)
{
int x,y;
split1(edit[id],ps,x,y);
edit[nw]=mg(mg(x,new_node(val)),y);
}
void del(int ps,int id,int nw)
{
int x,y,z;
split1(edit[id],ps-1,x,y);
split1(y,1,y,z);
edit[nw]=mg(x,z);
}
void reverse(int l,int r,int id,int nw)
{
int x,y,z;
split1(edit[id],l-1,x,y);
split1(y,r-l+1,y,z),nd[y].rev^=1;
edit[nw]=mg(mg(x,y),z);
}
void qry(int l,int r,int id,int nw)
{
int x,y,z;
edit[nw]=edit[id];
split1(edit[nw],l-1,x,y);
split1(y,r-l+1,y,z);
printf("%lld\n",las_ans=nd[y].sum);
}
int main()
{
rd(n);
int id,op;ll l,r;
for(int i=1;i<=n;i++)
{
rd(id),rd(op),rd(l);if(op!=2)rd(r);
l^=las_ans,r^=las_ans;
if(op==1)ins(l,r,id,i);
if(op==2)del(l,id,i);
if(op==3)reverse(l,r,id,i);
if(op==4)qry(l,r,id,i);
}
}
3.可持久化並查集
用可持久化線段樹維護每個點每個版本的父親,因爲不能加路徑壓縮所以只用按秩合併保證樹高在log,所以每次修改查到一個點父親要log^2的複雜度,總複雜度n*log^2(注意這題詢問數和節點數並不相同,空間要算好......)
#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define sc second
using namespace std;
const int N=2e5+100,M=8e6+100;
template<class T>
void rd(T &x)
{
char c=getchar();x=0;bool f=0;
while(!isdigit(c))f|=(c=='-'),c=getchar();
while(isdigit(c))x=x*10+c-48,c=getchar();
if(f)x=-x;
}
struct Seg{
int ls,rs,fa,ht;
}seg[M];
int n,m,tot=0;
int edit[N];
int build(int l,int r)
{
int nw=++tot;
if(l==r)
{
seg[nw].ls=seg[nw].rs=0;
seg[nw].fa=l,seg[nw].ht=1;
return nw;
}
int mid=(l+r)>>1;
seg[nw].ls=build(l,mid),seg[nw].rs=build(mid+1,r);
return nw;
}
pii qry(int nw,int l,int r,int to)
{
if(l==r)return pii(seg[nw].fa,seg[nw].ht);
int mid=(l+r)>>1;
if(to<=mid)return qry(seg[nw].ls,l,mid,to);
else return qry(seg[nw].rs,mid+1,r,to);
}
pii find_fa(int id,int x)
{
int bf=x;
pii nw=qry(edit[id],1,n,x);
while(nw.fi!=bf)
{
bf=nw.fi,
nw=qry(edit[id],1,n,nw.fi);
}
return nw;
}
int cg(int bf,int l,int r,int to,pii val)
{
int nw=++tot;
seg[nw]=seg[bf];
if(l==r)seg[nw].fa=val.fi,seg[nw].ht=val.sc;
else
{
int mid=(l+r)>>1;
if(to<=mid)seg[nw].ls=cg(seg[bf].ls,l,mid,to,val);
else seg[nw].rs=cg(seg[bf].rs,mid+1,r,to,val);
}
return nw;
}
int main()
{
int x,y,op;pii fx,fy;
rd(n),rd(m);
edit[0]=build(1,n);
for(int i=1;i<=m;i++)
{
rd(op),rd(x);if(op!=2)rd(y);
if(op==1)
{
fx=find_fa(i-1,x),fy=find_fa(i-1,y);
if(fx.sc>fy.sc)swap(fx,fy);
edit[i]=cg(edit[i-1],1,n,fx.fi,pii(fy.fi,fx.sc));
if(fx.sc==fy.sc)edit[i]=cg(edit[i],1,n,fy.fi,pii(fy.fi,fy.sc+1));
}
if(op==2)edit[i]=edit[x];
if(op==3)
{
edit[i]=edit[i-1];
fx=find_fa(i-1,x),fy=find_fa(i-1,y);
if(fx.fi==fy.fi)puts("1");
else puts("0");
}
}
}
4.可持久化trie樹
這個東西其實和線段樹平衡樹差不多,插入一個數多log個點,全都新開即可
題目比較多單獨寫了