线段树维护区间历史版本和
问题
一个数列,支持区间加、查询区间和、查询区间历史版本和。
解法一
设 \(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>