线段树维护区间历史版本和

问题#

一个数列,支持区间加、查询区间和、查询区间历史版本和。

解法一#

t 为当前时间,hiai 的历史版本和,维护一个值 ci=hitai

一个把 [l,r] 加上 v 的区间加操作,发现对于 i[l,r],cicitv(这里的 t 是操作之前的时间)。于是 ci 也用区间加维护即可。

点击查看代码
struct SegmentTree
{
	ll tag[maxn*4],tagh[maxn*4],sum[maxn*4],sumh[maxn*4],t;
	void addtag(ll p,ll l,ll r,ll v1,ll v2)
	{
		sum[p]+=(r-l+1)*v1;
		sumh[p]+=(r-l+1)*v2;
		tag[p]+=v1;
		tagh[p]+=v2;
	}
	void pushdown(ll p,ll l,ll r)
	{
		ll mid=l+r>>1;
		addtag(p<<1,l,mid,tag[p],tagh[p]);
		addtag(p<<1|1,mid+1,r,tag[p],tagh[p]);
		tag[p]=tagh[p]=0;
	}
	void modify(ll p,ll l,ll r,ll ql,ll qr,ll v1,ll v2)
	{
		if(ql<=l&&r<=qr)
		{
			addtag(p,l,r,v1,v2); return;
		}
		pushdown(p,l,r);
		ll mid=l+r>>1;
		if(ql<=mid) modify(p<<1,l,mid,ql,qr,v1,v2);
		if(mid<qr) modify(p<<1|1,mid+1,r,ql,qr,v1,v2);
		sum[p]=sum[p<<1]+sum[p<<1|1];
		sumh[p]=sumh[p<<1]+sumh[p<<1|1];
	}
	void modify(ll l,ll r,ll v)
	{
		modify(1,1,n,l,r,v,-v*t);
	}
	ll query(ll p,ll l,ll r,ll ql,ll qr)
	{
		if(ql<=l&&r<=qr) return sum[p]*t+sumh[p];
		if(qr<l||r<ql) return 0;
		pushdown(p,l,r);
		ll mid=l+r>>1;
		return query(p<<1,l,mid,ql,qr)+query(p<<1|1,mid+1,r,ql,qr);
	}
	ll query(ll l,ll r)
	{
		return query(1,1,n,l,r);
	}
	void upd()
	{
		++t;
	}
}T;

解法二#

对于普通的区间加,直接维护区间加法标记、区间和 tag,sum 即可。

每次操作完,考虑给全局打上一个“历史版本和更新标记”,即令 ansans+sum,设这个标记为 upd,普通加法标记为 add

线段树标记需要支持合并。对于两个标记队列 q1,q2,设 q2upd 标记个数为 cnt,不难发现,q1 标记更新后的 sumq2 中贡献了 cnt 次,然后 ans 还需要加上 q2 中加法标记单独的贡献,也就是“加法标记的历史版本和”,设为 tagh。于是我们只需要维护 cnt,tagh 即可代表一个标记队列。

维护三个标记 tag,cnt,tagh 即可。

点击查看代码
struct SegmentTree
{
	ll tag[maxn*4],tags[maxn*4],tagh[maxn*4],sum[maxn*4],sumh[maxn*4],t;
	void addtag(ll p,ll l,ll r,ll v)
	{
		sum[p]+=(r-l+1)*v; tag[p]+=v;
	}
	void addtagh(ll p,ll l,ll r,ll s,ll v)
	{
		tags[p]+=s; sumh[p]+=sum[p]*s+(r-l+1)*v; tagh[p]+=tag[p]*s+v;
	}
	void pushdown(ll p,ll l,ll r)
	{
		ll mid=l+r>>1;
		addtagh(p<<1,l,mid,tags[p],tagh[p]);
		addtagh(p<<1|1,mid+1,r,tags[p],tagh[p]);
		addtag(p<<1,l,mid,tag[p]);
		addtag(p<<1|1,mid+1,r,tag[p]);
		tag[p]=tags[p]=tagh[p]=0;
	}
	void modify(ll p,ll l,ll r,ll ql,ll qr,ll v)
	{
		if(ql<=l&&r<=qr)
		{
			addtag(p,l,r,v); return;
		}
		pushdown(p,l,r);
		ll mid=l+r>>1;
		if(ql<=mid) modify(p<<1,l,mid,ql,qr,v);
		if(mid<qr) modify(p<<1|1,mid+1,r,ql,qr,v);
		sum[p]=sum[p<<1]+sum[p<<1|1];
		sumh[p]=sumh[p<<1]+sumh[p<<1|1];
	}
	void modify(ll l,ll r,ll v)
	{
		modify(1,1,n,l,r,v);
	}
	ll query(ll p,ll l,ll r,ll ql,ll qr)
	{
		if(ql<=l&&r<=qr) return sumh[p];
		if(qr<l||r<ql) return 0;
		pushdown(p,l,r);
		ll mid=l+r>>1;
		return query(p<<1,l,mid,ql,qr)+query(p<<1|1,mid+1,r,ql,qr);
	}
	ll query(ll l,ll r)
	{
		return query(1,1,n,l,r);
	}
	void upd()
	{
		addtagh(1,1,n,1,0);
	}
}T;

一个题#

考虑离线询问,扫描线。

枚举 r,设 fA[l],fB[l]maxlir{Ai},maxlir{Bi},然后对于询问 (L,R),右端点 r=R 的答案为 l=LrfA[i]fB[i]

首先,更新 fA,fB 的时间是 O(n2) 的。考虑单调栈,于是我们只需要支持区间覆盖 fA,fB 即可。

然后我们可以用线段树维护单个 r 的贡献 l=LrfA[i]fB[i]

如果是 r=LR,可以考虑线段树维护这个 [L,R] 区间的历史版本和。

维护历史版本和难以支持区间覆盖,考虑到单调栈比较特殊,可以把覆盖转化为区间加。

然后类似于解法二,我们需要维护这些标记。

发现对于两个标记队列 q1,q2,由于 ans=sum=i=lrfA[i]fB[i],这些 fA[i],fB[i] 分为 不是因 q2 中加法标记更新的 和 q2 中加法标记更新的 两部分,即 i=lr(fA1[i]+tagA2)(fB1[i]+tagB2),考虑拆开,四部分分别为 fA1[i]fB1[i], fA1[i]taghB2, fB1[i]taghA2, taghA2taghB2,其中 taghA2,taghB2q2A,B 加法标记的历史版本和。

taghAB=taghAtaghB,那么合并后的 taghAB 应为 taghAB=tagAtagB=taghAB1+tagA1taghB2+tagB1taghA1+taghAB2

于是维护六个标记 tagA,tagB,cnt,taghA,taghB,taghAB 即可。

点击查看代码
#include<bits/stdc++.h>
#define pr pair<ll,ll>
#define x first
#define y second
#define mkp(a,b) make_pair(a,b)
#define pb push_back
#define ll long long
#define ull unsigned ll
using namespace std;
const ll maxn=3e5+10;
ll n,q,A[maxn],B[maxn],l,r;
ull ans[maxn];
vector<pr>vec[maxn];
struct SegmentTree
{
	ull tagA[maxn*4],tagB[maxn*4],sumA[maxn*4],sumB[maxn*4],sum[maxn*4],sumh[maxn*4],taghAB[maxn*4],taghA[maxn*4],taghB[maxn*4],cnt[maxn*4];
	void addtag(ll p,ll l,ll r,ll vA,ll vB)
	{
		sum[p]+=sumB[p]*vA;
		sumA[p]+=(r-l+1)*vA;
		tagA[p]+=vA;
	
		sum[p]+=sumA[p]*vB;
		sumB[p]+=(r-l+1)*vB;
		tagB[p]+=vB;
	}
	void addtagh(ll p,ll l,ll r,ll _taghA,ll _taghB,ll _taghAB,ll _cnt)
	{
		sumh[p]+=sum[p]*_cnt+sumA[p]*_taghB+sumB[p]*_taghA+_taghAB*(r-l+1);
		cnt[p]+=_cnt;
		taghA[p]+=tagA[p]*_cnt+_taghA;
		taghB[p]+=tagB[p]*_cnt+_taghB;
		taghAB[p]+=tagA[p]*tagB[p]*_cnt+tagA[p]*_taghB+tagB[p]*_taghA+_taghAB;
	}
	void pushdown(ll p,ll l,ll r)
	{
		ll mid=l+r>>1;
		addtagh(p<<1,l,mid,taghA[p],taghB[p],taghAB[p],cnt[p]);
		addtagh(p<<1|1,mid+1,r,taghA[p],taghB[p],taghAB[p],cnt[p]);
		addtag(p<<1,l,mid,tagA[p],tagB[p]);
		addtag(p<<1|1,mid+1,r,tagA[p],tagB[p]);
		tagA[p]=tagB[p]=taghA[p]=taghB[p]=taghAB[p]=cnt[p]=0;
	}
	void modify(ll p,ll l,ll r,ll ql,ll qr,ll vA,ll vB)
	{
		if(ql<=l&&r<=qr)
		{
			addtag(p,l,r,vA,vB); return;
		}
		pushdown(p,l,r);
		ll mid=l+r>>1;
		if(ql<=mid) modify(p<<1,l,mid,ql,qr,vA,vB);
		if(mid<qr) modify(p<<1|1,mid+1,r,ql,qr,vA,vB);
		sum[p]=sum[p<<1]+sum[p<<1|1];
		sumA[p]=sumA[p<<1]+sumA[p<<1|1];
		sumB[p]=sumB[p<<1]+sumB[p<<1|1];
		sumh[p]=sumh[p<<1]+sumh[p<<1|1];
	}
	void modifyA(ll l,ll r,ll v)
	{
		modify(1,1,n,l,r,v,0);
	}
	void modifyB(ll l,ll r,ll v)
	{
		modify(1,1,n,l,r,0,v);
	}
	ll query(ll p,ll l,ll r,ll ql,ll qr)
	{
		if(ql<=l&&r<=qr) return sumh[p];
		if(qr<l||r<ql) return 0;
		pushdown(p,l,r);
		ll mid=l+r>>1;
		return query(p<<1,l,mid,ql,qr)+query(p<<1|1,mid+1,r,ql,qr);
	}
	ll query(ll l,ll r)
	{
		return query(1,1,n,l,r);
	}
	void upd()
	{
		addtagh(1,1,n,0,0,0,1);
	}
}T;
ll stkA[maxn],topA,stkB[maxn],topB;
int main()
{
//	freopen("match.in","r",stdin);
//	freopen("match.out","w",stdout);
	scanf("%*lld%lld",&n);
	for(ll i=1;i<=n;i++) scanf("%lld",A+i);
	for(ll i=1;i<=n;i++) scanf("%lld",B+i);
	scanf("%lld",&q);
	for(ll i=1;i<=q;i++)
	{
		scanf("%lld%lld",&l,&r);
		vec[r].push_back(mkp(l,i));
	}
	for(ll i=1;i<=n;i++)
	{
		while(topA&&A[stkA[topA]]<A[i])
		{
			T.modifyA(stkA[topA-1]+1,stkA[topA],-A[stkA[topA]]);
			--topA;
		}
		T.modifyA(stkA[topA]+1,i,A[i]);
		while(topB&&B[stkB[topB]]<B[i])
		{
			T.modifyB(stkB[topB-1]+1,stkB[topB],-B[stkB[topB]]);
			--topB;
		}
		T.modifyB(stkB[topB]+1,i,B[i]);
		T.upd();
		stkA[++topA]=i;
		stkB[++topB]=i;
		for(ll j=0;j<vec[i].size();j++)
		{
			ll k=vec[i][j].x, id=vec[i][j].y;
			ans[id]=T.query(k,i);
		}
	}
	for(ll i=1;i<=q;i++)
		printf("%llu\n",ans[i]);
	return 0;
}
</details>

出处:https://www.cnblogs.com/Sktn0089/p/17755836.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Lgx_Q  阅读(1544)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示