ybtAu「高级数据结构」第6章 可持久化数据结构
A. 【例题1】区间第k小
主席树板子。注意需要离散化。
#include <iostream>
#include <algorithm>
#include <vector>
#define N 100005
int n,m,rt[N],a[N];
std::vector<int> b;
namespace SGT
{
int d[N<<5],ls[N<<5],rs[N<<5],idx;
#define mid (lb+rb>>1)
int nd(int x) {int u=++idx;return d[u]=d[x],ls[u]=ls[x],rs[u]=rs[x],u;}
int md(int x,int t,int lb,int rb)
{
int u=nd(x);
d[u]++;
if(lb<rb) (t<=mid)?(ls[u]=md(ls[u],t,lb,mid)):(rs[u]=md(rs[u],t,mid+1,rb));
return u;
}
int qr(int x,int y,int k,int lb,int rb)
{
if(lb==rb) return lb;
int dt=d[ls[x]]-d[ls[y]];
return (k<=dt)?qr(ls[x],ls[y],k,lb,mid):qr(rs[x],rs[y],k-dt,mid+1,rb);
}
#undef mid
};
signed main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0),std::cout.tie(0);
std::cin>>n>>m;
for(int i=1;i<=n;i++) std::cin>>a[i],b.push_back(a[i]);
std::sort(b.begin(),b.end());
b.erase(std::unique(b.begin(),b.end()),b.end());
int len=b.size();
for(int i=1;i<=n;i++)
{
a[i]=std::lower_bound(b.begin(),b.end(),a[i])-b.begin();
rt[i]=SGT::md(rt[i-1],a[i],0,len);
}
for(int i=1,l,r,k;i<=m;i++)
{
std::cin>>l>>r>>k;
std::cout<<b[SGT::qr(rt[r],rt[l-1],k,0,len)]<<'\n';
}
}
B. 【例题2】可持久化平衡树
如题。
#include <iostream>
#include <cstdlib>
#define N 500005
int n,rt[N];
namespace FHQ
{
int idx,ls[N<<6],rs[N<<6];
#define pii std::pair<int,int>
struct Node
{
int val,wt,siz,rep;
} tr[N<<6];
int nd(int x) {int u=++idx;return tr[u]=tr[x],tr[u].wt=rand(),ls[u]=ls[x],rs[u]=rs[x],u;}
void pu(int x) {tr[x].siz=tr[ls[x]].siz+tr[rs[x]].siz+tr[x].rep;}
pii split1(int x,int t)
{
if(!x) return {0,0};
int u=nd(x);
if(t<tr[u].val) {pii tmp=split1(ls[u],t);ls[u]=tmp.second,pu(u);return {tmp.first,u};}
else {pii tmp=split1(rs[u],t);rs[u]=tmp.first,pu(u);return {u,tmp.second};}
}
int merge(int x,int y)
{
if(!x||!y) return x|y;
if(tr[x].wt<tr[y].wt) return rs[x]=merge(rs[x],y),pu(x),x;
else return ls[y]=merge(x,ls[y]),pu(y),y;
}
// void print(int x)
// {
// if(!x) return;
// print(ls[x]),printf("%d ",tr[x].val),print(rs[x]);
// }
void ins(int &x,int t)
{
pii tmp=split1(x,t),tmp1=split1(tmp.first,t-1);
//print(tmp1.first),printf(" t1first\n"),print(tmp1.second),printf(" t1second\n"),print(tmp.second),printf(" tsecond\n");
int nx=tmp1.second;
if(!nx) nx=++idx,tr[nx]={t,rand(),1,1};
else tr[nx].rep++,tr[nx].siz++;
x=merge(merge(tmp1.first,nx),tmp.second);
}
void del(int &x,int t)
{
pii tmp=split1(x,t),tmp1=split1(tmp.first,t-1);
int nx=tmp1.second;
if(tr[nx].rep>1) tr[nx].rep--,tr[nx].siz--,tmp1.first=merge(tmp1.first,nx);
x=merge(tmp1.first,tmp.second);
}
int qrk(int &x,int t)
{
pii tmp=split1(x,t-1);
int ret=tr[tmp.first].siz;
x=merge(tmp.first,tmp.second);
return ret;
}
int qnum(int &x,int t)
{
t++;
int p=x;
while(p)
{
if(t<=tr[ls[p]].siz) p=ls[p];
else if(t<=tr[ls[p]].siz+tr[p].rep) return tr[p].val;
else t-=tr[ls[p]].siz+tr[p].rep,p=rs[p];
}
return 0;
}
int qpre(int &x,int t)
{
pii tmp=split1(x,t-1);
int p=tmp.first;
while(rs[p]) p=rs[p];
int ret=tr[p].val;
x=merge(tmp.first,tmp.second);
return ret;
}
int qnxt(int &x,int t)
{
pii tmp=split1(x,t);
int p=tmp.second;
while(ls[p]) p=ls[p];
int ret=tr[p].val;
x=merge(tmp.first,tmp.second);
return ret;
}
};
signed main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0),std::cout.tie(0);
std::cin>>n;
FHQ::ins(rt[0],-2147483647),FHQ::ins(rt[0],2147483647);
for(int i=1,v,op,x;i<=n;i++)
{
std::cin>>v>>op>>x,rt[i]=rt[v];
if(op==1) FHQ::ins(rt[i],x);
if(op==2) FHQ::del(rt[i],x);
if(op==3) std::cout<<FHQ::qrk(rt[i],x)<<'\n';
if(op==4) std::cout<<FHQ::qnum(rt[i],x)<<'\n';
if(op==5) std::cout<<FHQ::qpre(rt[i],x)<<'\n';
if(op==6) std::cout<<FHQ::qnxt(rt[i],x)<<'\n';
}
}
C. 【例题3】可持久化数组
可持久化线段树实现即可。
#include <iostream>
#define N 1000005
int n,m,a[N],rt[N];
namespace SGT
{
int d[N<<5],ls[N<<5],rs[N<<5],idx;
#define mid (lb+rb>>1)
int build(int lb,int rb)
{
int x=++idx;
if(lb==rb) return d[x]=a[lb],x;
return ls[x]=build(lb,mid),rs[x]=build(mid+1,rb),x;
}
int md(int x,int t,int k,int lb,int rb)
{
int u=++idx;
ls[u]=ls[x],rs[u]=rs[x];
if(lb==rb) d[u]=k;
else (t<=mid)?(ls[u]=md(ls[u],t,k,lb,mid)):(rs[u]=md(rs[u],t,k,mid+1,rb));
return u;
}
int qr(int x,int t,int lb,int rb)
{
if(lb==rb) return d[x];
return (t<=mid)?qr(ls[x],t,lb,mid):qr(rs[x],t,mid+1,rb);
}
#undef mid
};
signed main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0),std::cout.tie(0);
std::cin>>n>>m;
for(int i=1;i<=n;i++) std::cin>>a[i];
rt[0]=SGT::build(1,n);
for(int i=1,v,op,x,t;i<=m;i++)
{
std::cin>>v>>op>>x;
if(op==1) std::cin>>t,rt[i]=SGT::md(rt[v],x,t,1,n);
if(op==2) std::cout<<SGT::qr(rt[i]=rt[v],x,1,n)<<'\n';
}
}
D. 【例题4】可持久化并查集
需要按秩合并,可持久化线段树维护 \(fa\) 和 \(height\),\(find\) 时求父节点需要在线段树上查询,合并需要把 \(height\) 小的合并到 \(height\) 大的上。注意只有在修改父亲时需要新建版本。
#include <iostream>
#define N 200005
int n,m,rt[N];
namespace SGT
{
int fa[N<<5],h[N<<5],ls[N<<5],rs[N<<5],idx;
#define mid (lb+rb>>1)
int build(int lb,int rb)
{
int x=++idx;
if(lb==rb) fa[x]=lb,h[x]=1;
else ls[x]=build(lb,mid),rs[x]=build(mid+1,rb);
return x;
}
int getid(int x,int t,int lb,int rb)
{
if(lb==rb) return x;
return (t<=mid)?getid(ls[x],t,lb,mid):getid(rs[x],t,mid+1,rb);
}
int md(int x,int t,int k,int lb,int rb)
{
int u=++idx;
ls[u]=ls[x],rs[u]=rs[x],h[u]=h[x];
if(lb==rb) fa[u]=k;
else (t<=mid)?(ls[u]=md(ls[u],t,k,lb,mid)):(rs[u]=md(rs[u],t,k,mid+1,rb));
return u;
}
void addep(int x,int t,int lb,int rb)
{
if(lb==rb) h[x]++;
else (t<=mid)?addep(ls[x],t,lb,mid):addep(rs[x],t,mid+1,rb);
}
int gf(int x,int t)
{
int p=getid(x,t,1,n);
if(t!=fa[p]) return gf(x,fa[p]);
else return t;
}
int merge(int x,int u,int v)
{
u=gf(x,u),v=gf(x,v);
if(u==v) return x;
if(h[u]>h[v]) std::swap(u,v);
x=md(x,u,v,1,n);
if(h[u]==h[v]) addep(x,u,1,n);
return x;
}
#undef mid
};
signed main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0),std::cout.tie(0);
std::cin>>n>>m;
rt[0]=SGT::build(1,n);
for(int i=1,op,x,y;i<=m;i++)
{
std::cin>>op>>x,rt[i]=rt[i-1];
if(op==1) std::cin>>y,rt[i]=SGT::merge(rt[i],x,y);
if(op==2) rt[i]=rt[x];
if(op==3) std::cin>>y,std::cout<<(SGT::gf(rt[i],x)==SGT::gf(rt[i],y))<<'\n';
}
}
E. 区间众数
分块
主席树,如果某节点左边区间的出现次数大于 \(\lfloor\frac{r-l+1}2\rfloor\),那么往左儿子走;如果右边区间的出现次数大于 \(\lfloor\frac{r-l+1}2\rfloor\),那么往右儿子走;如果都不满足,那么说明没有众数。
#include <iostream>
#define N 300005
int n,m,L,a[N],rt[N];
namespace SGT
{
int d[N<<5],ls[N<<5],rs[N<<5],idx;
#define mid (lb+rb>>1)
int md(int x,int t,int lb,int rb)
{
int u=++idx;
d[u]=d[x]+1,ls[u]=ls[x],rs[u]=rs[x];
if(lb<rb) (t<=mid)?(ls[u]=md(ls[u],t,lb,mid)):(rs[u]=md(rs[u],t,mid+1,rb));
return u;
}
int qr(int x,int y,int k,int lb,int rb)
{
if(lb==rb) return (d[x]-d[y]>k)?lb:-1;
if(d[ls[x]]-d[ls[y]]>k) return qr(ls[x],ls[y],k,lb,mid);
if(d[rs[x]]-d[rs[y]]>k) return qr(rs[x],rs[y],k,mid+1,rb);
return -1;
}
#undef mid
};
signed main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0),std::cout.tie(0);
std::cin>>n>>L;
for(int i=1;i<=n;i++) std::cin>>a[i],rt[i]=SGT::md(rt[i-1],a[i],1,L);
std::cin>>m;
for(int i=1,l,r;i<=m;i++)
{
std::cin>>l>>r;
int ans=SGT::qr(rt[r],rt[l-1],r-l+1>>1,1,L);
if(ans==-1) std::cout<<"no\n";
else std::cout<<"yes "<<ans<<'\n';
}
}
F. 树上第K小
树上主席树,每个节点从其父节点的版本修改即可。
#include <iostream>
#include <vector>
#include <algorithm>
#define N 300005
#define int long long
int n,m,a[N],rt[N],hed[N],tal[N<<1],nxt[N<<1],cnte,len;
void de(int u,int v) {tal[++cnte]=v,nxt[cnte]=hed[u],hed[u]=cnte;}
std::vector<int> b;
namespace SGT
{
int d[N<<5],ls[N<<5],rs[N<<5],idx;
#define mid (lb+rb>>1)
int md(int x,int t,int lb,int rb)
{
int u=++idx;
d[u]=d[x]+1,ls[u]=ls[x],rs[u]=rs[x];
if(lb<rb) (t<=mid)?(ls[u]=md(ls[u],t,lb,mid)):(rs[u]=md(rs[u],t,mid+1,rb));
return u;
}
int qr(int x,int y,int u,int v,int k,int lb,int rb)
{
if(lb==rb) return lb;
int dt=d[ls[x]]+d[ls[y]]-d[ls[u]]-d[ls[v]];
return (k<=dt)?qr(ls[x],ls[y],ls[u],ls[v],k,lb,mid):qr(rs[x],rs[y],rs[u],rs[v],k-dt,mid+1,rb);
}
#undef mid
};
namespace HLD
{
int dep[N],fa[N],son[N],siz[N],top[N],idx;
void dfs1(int x)
{
siz[x]=1,rt[x]=SGT::md(rt[fa[x]],a[x],0,len-1);
for(int i=hed[x];i;i=nxt[i]) if(!siz[tal[i]])
{
fa[tal[i]]=x,dep[tal[i]]=dep[x]+1,dfs1(tal[i]),siz[x]+=siz[tal[i]];
if(siz[tal[i]]>siz[son[x]]) son[x]=tal[i];
}
}
void dfs2(int x,int tp)
{
if(!x) return;
dfs2(son[x],top[x]=tp);
for(int i=hed[x];i;i=nxt[i]) if(!top[tal[i]]) dfs2(tal[i],tal[i]);
}
int lca(int x,int y)
{
while(top[x]!=top[y]) (dep[top[x]]<dep[top[y]])?(y=fa[top[y]]):(x=fa[top[x]]);
return dep[x]<dep[y]?x:y;
}
int qr(int x,int y,int k) {int t=lca(x,y);return SGT::qr(rt[x],rt[y],rt[t],rt[fa[t]],k,0,len-1);}
};
signed main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0),std::cout.tie(0);
std::cin>>n>>m;
for(int i=1;i<=n;i++) std::cin>>a[i],b.push_back(a[i]);
std::sort(b.begin(),b.end());
b.erase(std::unique(b.begin(),b.end()),b.end());
len=b.size();
for(int i=1;i<=n;i++) a[i]=std::lower_bound(b.begin(),b.end(),a[i])-b.begin();
for(int i=1,u,v;i<n;i++) std::cin>>u>>v,de(u,v),de(v,u);
HLD::dfs1(1),HLD::dfs2(1,1);
int la=0;
for(int i=1,u,v,k;i<=m;i++)
{
std::cin>>u>>v>>k;
std::cout<<(la=b[HLD::qr(u^la,v,k)])<<'\n';
}
}
G. 最大中位数
看到最大,又看到中位数,不难想到二分答案,把小于 \(mid\) 的置为 \(-1\),大于等于 \(mid\) 的置为 \(1\)。
\([b+1,c-1]\) 这一段是一定要选的。取 \([a,b]\) 的最大后缀,取 \([c,d]\) 的最大前缀,与 \([b+1,c-1]\) 的区间和相加判断是否 \(\ge0\) 即可。
现在需要对每个 \(mid\) 快速求出符合条件的数组。发现当 \(mid\rightarrow mid+1\) 时,只有等于 \(mid\) 的数会发生变化,而其他不变,所以考虑使用主席树。
查询时在对应 \(mid\) 的版本的线段树上求区间和、区间最大前后缀,容易实现。
#include <iostream>
#include <algorithm>
#include <vector>
#define N 20005
int n,q,a[N],rt[N],len;
std::vector<int> b;
int li[N];
struct Node {int d,pre,suf;};
bool cmp(int x,int y) {return a[x]<a[y];}
namespace SGT
{
Node tr[N<<5];
int ls[N<<5],rs[N<<5],idx;
#define mid (lb+rb>>1)
Node mg(Node x,Node y)
{
Node ret;
ret.d=x.d+y.d;
ret.pre=std::max(x.pre,x.d+y.pre);
ret.suf=std::max(y.suf,y.d+x.suf);
return ret;
}
int md(int x,int t,int k,int lb,int rb)
{
int u=++idx;ls[u]=ls[x],rs[u]=rs[x];
if(lb==rb) return tr[u]={k,std::max(k,0),std::max(k,0)},u;
(t<=mid)?(ls[u]=md(ls[u],t,k,lb,mid)):(rs[u]=md(rs[u],t,k,mid+1,rb));
return tr[u]=mg(tr[ls[u]],tr[rs[u]]),u;
}
Node qr(int x,int l,int r,int lb,int rb)
{
if(l<=lb&&rb<=r) return tr[x];
if(r<=mid) return qr(ls[x],l,r,lb,mid);
if(l>mid) return qr(rs[x],l,r,mid+1,rb);
return mg(qr(ls[x],l,r,lb,mid),qr(rs[x],l,r,mid+1,rb));
}
#undef mid
};
signed main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0),std::cout.tie(0);
std::cin>>n;
for(int i=1;i<=n;i++) std::cin>>a[i],b.push_back(a[i]),li[i]=i;
std::sort(b.begin(),b.end());
b.erase(std::unique(b.begin(),b.end()),b.end());
len=b.size();
for(int i=1;i<=n;i++) a[i]=std::lower_bound(b.begin(),b.end(),a[i])-b.begin();
std::sort(li+1,li+n+1,cmp);
for(int i=1;i<=n;i++) rt[0]=SGT::md(rt[0],i,1,1,n);
int tmp=0;
for(int i=1;i<=n;i++) rt[a[li[i]]+1]=SGT::md(rt[tmp],li[i],-1,1,n),tmp=a[li[i]]+1;
std::cin>>q;
int la=0;
for(int i=1,w,x,y,z;i<=q;i++)
{
std::vector<int> t(4);
for(int j=0;j<4;j++) std::cin>>t[j],t[j]=(t[j]+la)%n;
std::sort(t.begin(),t.end());
w=t[0]+1,x=t[1]+1,y=t[2]+1,z=t[3]+1;
int l=0,r=len,ans=-1;
while(l<=r)
{
int mid=l+r>>1;
Node n1=SGT::qr(rt[mid],x,y,1,n),n2=SGT::qr(rt[mid],w,x-1,1,n),n3=SGT::qr(rt[mid],y+1,z,1,n);
if(n1.d+n2.suf+n3.pre>=0) l=mid+1,ans=mid;
else r=mid-1;
}
std::cout<<(la=b[ans])<<'\n';
}
}

浙公网安备 33010602011771号