可持久化线段树

Part1:线段树

思路:通过 \(O(logn)\) 的复杂度实现区间修改与查询

大概意思就是建一棵二叉树,节点代表一个区间,左儿子代表区间前一半,右儿子代表后一半,这样就可以将区间拆成log个区间拼在一起

模版 Luogu P3372

#include<bits/stdc++.h>
using namespace std;
#define N 500010
int n,m;
long long a[N],b[N*2],d[N*2];
void build(int s,int t,int p)
{
	if(s==t) d[p]=a[s];
	else 
	{
		int m=s+((t-s)>>1);
		build(s,m,p*2),build(m+1,t,p*2+1);
		d[p]=d[p*2]+d[p*2+1];
	}
}
void update(int l,int r,int s,int t,int p,int v)
{
	if(l<=s&&t<=r)
	{
		d[p]+=(t-s+1)*v;
		b[p]+=v;
		return;
	}
	int m=s+((t-s)>>1);
	if(b[p]&&s!=t)
	{
		d[p*2]+=b[p]*(m-s+1),d[p*2+1]+=b[p]*(t-m);
		b[p*2]+=b[p],b[p*2+1]+=b[p];
		b[p]=0;
	}
	if(l<=m) update(l,r,s,m,p*2,v);
	if(r>m) update(l,r,m+1,t,p*2+1,v);
	d[p]=d[p*2]+d[p*2+1];
}
long long getsum(int l,int r,int s,int t,int p)
{
	if(l<=s&&t<=r) return d[p];
	int m=s+((t-s)>>1);
	long long ans=0;
	if(b[p])
	{
	    d[p*2]+=b[p]*(m-s+1),d[p*2+1]+=b[p]*(t-m);
	    b[p*2]+=b[p],b[p * 2 + 1] += b[p];
	    b[p]=0;
  	}
	if(l<=m) ans+=getsum(l,r,s,m,p*2);
	if(r>m) ans+=getsum(l,r,m+1,t,p*2+1);
	return ans;
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	build(1,n,1);
	for(int i=1;i<=m;i++)
	{
		int ty,l,r,w;
		cin>>ty>>l>>r;
		if(ty==1)
		{
			cin>>w;
			update(l,r,1,n,1,w);
		}
		else
			cout<<getsum(l,r,1,n,1)<<'\n'; 
	}
	return 0;
}

Part2:可持久化线段树

例题 静态区间第k小

这道题我们可以开n棵线段树,第i棵代表 \(a_1-a_i\) 的分布,然后利用前缀和,将 \(r\) 的分布减去 \(l-1\) 的分布就是 \(l-r\) 的分布

但是这样的空间是 \(2n^2\),会炸掉,所以考虑用可持久化线段树优化空间

因为从 \(i-1\) 到 \(i\) 只会使一条链变化(因为只会更改一个叶子),所以只需要在原树基础上再开一条新链就可以了

#include<bits/stdc++.h>
using namespace std;
const int N=200010;
#define mid ((l+r)>>1)
int n,m,a[N],b[N],q,l,r,k;
int root[N],tot;
int ls[N*25],rs[N*25],sum[N*25];
void build(int &u,int l,int r)
{
	u=++tot;
	if(l==r) return;
	build(ls[u],l,mid);
	build(rs[u],mid+1,r);
	return;
}
void change(int &u,int pre,int l,int r,int p)
{
	u=++tot;
	sum[u]=sum[pre]+1,ls[u]=ls[pre],rs[u]=rs[pre];
	if(l==r) return;
	if(p<=mid) change(ls[u],ls[pre],l,mid,p);
	else change(rs[u],rs[pre],mid+1,r,p);
}
int query(int s,int t,int l,int r,int k)
{
	if(l==r) return l;
	int x=sum[ls[t]]-sum[ls[s]];
	if(k<=x) return query(ls[s],ls[t],l,mid,k);
	else return query(rs[s],rs[t],mid+1,r,k-x);
}
int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		b[i]=a[i];
	}
	sort(b+1,b+n+1);
	q=unique(b+1,b+n+1)-b-1;
	build(root[0],1,q);
	for(int i=1;i<=n;i++)
	{
		int x=lower_bound(b+1,b+q+1,a[i])-b;
		change(root[i],root[i-1],1,q,x);
	}
	for(int i=1;i<=m;i++)
	{
		cin>>l>>r>>k;
		cout<<b[query(root[l-1],root[r],1,q,k)]<<'\n';
	}
	return 0;
}
posted @ 2026-10-30 14:51  Rainesxiao  阅读(4)  评论(0)    收藏  举报