Loading

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';
	}
}

H. 远古山脉

posted @ 2025-06-30 16:35  整齐的艾萨克  阅读(6)  评论(0)    收藏  举报