Loading

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

问题

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

解法一

\(t\) 为当前时间,\(h_i\)\(a_i\) 的历史版本和,维护一个值 \(c_i=h_i-t\cdot a_i\)

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

点击查看代码
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\) 即可。

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

线段树标记需要支持合并。对于两个标记队列 \(q_1,q_2\),设 \(q_2\)\(upd\) 标记个数为 \(cnt\),不难发现,\(q_1\) 标记更新后的 \(sum\)\(q_2\) 中贡献了 \(cnt\) 次,然后 \(ans\) 还需要加上 \(q_2\) 中加法标记单独的贡献,也就是“加法标记的历史版本和”,设为 \(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]\)\(\max\limits_{l\le i\le r}\{A_i\},\max\limits_{l\le i\le r}\{B_i\}\),然后对于询问 \((L,R)\),右端点 \(r=R\) 的答案为 \(\sum\limits_{l=L}^r fA[i]\cdot fB[i]\)

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

然后我们可以用线段树维护单个 \(r\) 的贡献 \(\sum\limits_{l=L}^r fA[i]\cdot fB[i]\)

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

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

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

发现对于两个标记队列 \(q_1,q_2\),由于 \(ans=\sum sum=\sum \sum\limits_{i=l}^r fA[i]\cdot fB[i]\),这些 \(fA[i],fB[i]\) 分为 不是因 \(q_2\) 中加法标记更新的 和 \(q_2\) 中加法标记更新的 两部分,即 \(\sum \sum\limits_{i=l}^r (fA_1[i]+tagA_2)(fB_1[i]+tagB_2)\),考虑拆开,四部分分别为 \(fA_1[i]\cdot fB_1[i],\space fA_1[i]\cdot taghB_2,\space fB_1[i]\cdot taghA_2,\space taghA_2\cdot taghB_2\),其中 \(taghA_2,taghB_2\)\(q_2\)\(A,B\) 加法标记的历史版本和。

\(taghAB=taghA\cdot taghB\),那么合并后的 \(taghAB'\) 应为 \(taghAB'=\sum tagA\cdot tagB=taghAB_1+tagA_1\cdot taghB_2+tagB_1\cdot taghA_1+taghAB_2\)

于是维护六个标记 \(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>
posted @ 2023-10-10 21:44  Lgx_Q  阅读(802)  评论(2编辑  收藏  举报