https://www.luogu.com.cn/problem/P8868
我学会了历史和!
在一阵扫描线过后,你会发现,[l,r] 的所有子区间的答案,就一定是扫到 i 的时候,加上 [k,i] 的答案,k≤i,i∈[l,r],然后又因为只有当 i≥l 的时候,才能对左端点在 [l,r] 的答案贡献,因此,你会发现这个东西就是一个扫过去,然后区间加法,然后查询区间历史和的问题。
即对于 [l,r],我们需要知道 [l′,r′]⊂[l,r] 的答案,那么我们可以扫描线,扫到 i 的时候,用序列的第 j 位表示 [j,i] 的答案。因为 i 只会更新 j≤i,因此当扫到 i<l 的时候,对答案无影响。当 i≥l 的时候,会更新左端点的答案,此时若左端点在范围内,显然是要贡献给答案的,注意到,左端点的限制是仅跟查询的区间有关的,而对于一个询问 [l,r] 我们可以把它挂在 r 上,那么需要往后一位扫,更新,贡献,往后一位扫,更新,贡献……这样的一个过程,对于贡献抽离出来,发现就是历史和问题。
总结一下,对于抽象到二维平面问题的矩形和,我们可以利用扫描线,然后维护更新操作,那么答案一定某些左端点的历史答案的和,这些左端点仅跟询问的区间有关
具体一下就是 [l,r] 的子区间有啥呢?
[l,l]
[l,l+1],[l+1,l+1]
[l,l+2],[l+1,l+2],[l+2,l+2]
...
[l,r],[l+1,r],[l+2,r]...[r−1,r],[r,r]
如果说扫到某个位置,比如 l+2,那么比如说 [l,l+2] 的答案是贡献到 l 上的。
那么对于 [l,l],[l,l+1],[l,l+2],...,[l,r] 这些,你会发现就是扫到 r 后 l 的历史和。
嗯!
然后历史和,就是在每次更新后,将当前的答案,贡献给历史和,你就把矩阵乘法的形式写一写,然后嗯算哪些位置可能非零,然后抽离出来大力算。
然后在维护的时候不要想一步到位,抽离出来每一个步骤比较好做。比如你要时刻维护历史和,那你可以看成更新完,然后再来一个维护历史和的操作,而不是在更新的过程中就顺带维护了历史和。
更新后必须的更新历史和。
[Ans,val,Sa,Sb,len]→[Ans+val,val,Sa,Sb,len]
更新 A。
[Ans,val,Sa,Sb,len]→[Ans,v⋅Sb,v⋅len,Sb,len]
更新 B 同理。
#include <bits/stdc++.h>
#define ull unsigned long long
#define ls ((cur)<<1)
#define rs ((ls)|1)
#define pb push_back
using namespace std;
const int N=(int)(2.5e5+5);
vector<pair<int,int> >vec[N];
ull ans[N];
int La[N],Lb[N],tp,st[N],n,q,a[N],b[N];
struct Matt {
ull a[5];
Matt() {
memset(a,0,sizeof(a));
}
void clr() {
memset(a,0,sizeof(a));
}
}val[N<<2];
struct Mat {
ull a[14];
Mat() {
memset(a,0,sizeof(a));
}
void clr() {
memset(a,0,sizeof(a));
}
void clr1() {
clr();
a[0]=a[2]=a[5]=a[8]=a[13]=1;
}
}tag[N<<2],P;
Matt Mul(const Matt &f,const Mat &g) {
Matt res;
res.a[0]=1llu*f.a[0]*g.a[0]+1llu*f.a[1]*g.a[1]+1llu*f.a[2]*g.a[3]+1llu*f.a[3]*g.a[6]+1llu*f.a[4]*g.a[9];
res.a[1]=1llu*f.a[1]*g.a[2]+1llu*f.a[2]*g.a[4]+1llu*f.a[3]*g.a[7]+1llu*f.a[4]*g.a[10];
res.a[2]=1llu*f.a[2]*g.a[5]+1llu*f.a[4]*g.a[11];
res.a[3]=1llu*f.a[3]*g.a[8]+1llu*f.a[4]*g.a[12];
res.a[4]=1llu*f.a[4]*g.a[13];
return res;
}
Mat mul(const Mat &f,const Mat &g) {
Mat res;
res.a[0]=1llu*f.a[0]*g.a[0];
res.a[1]=1llu*f.a[1]*g.a[0]+1llu*f.a[2]*g.a[1];
res.a[2]=1llu*f.a[2]*g.a[2];
res.a[3]=1llu*f.a[3]*g.a[0]+1llu*f.a[4]*g.a[1]+1llu*f.a[5]*g.a[3];
res.a[4]=1llu*f.a[4]*g.a[2]+1llu*f.a[5]*g.a[4];
res.a[5]=1llu*f.a[5]*g.a[5];
res.a[6]=1llu*f.a[6]*g.a[0]+1llu*f.a[7]*g.a[1]+1llu*f.a[8]*g.a[6];
res.a[7]=1llu*f.a[7]*g.a[2]+1llu*f.a[8]*g.a[7];
res.a[8]=1llu*f.a[8]*g.a[8];
res.a[9]=1llu*f.a[9]*g.a[0]+1llu*f.a[10]*g.a[1]+1llu*f.a[11]*g.a[3]+1llu*f.a[12]*g.a[6]+1llu*f.a[13]*g.a[9];
res.a[10]=1llu*f.a[10]*g.a[2]+1llu*f.a[11]*g.a[4]+1llu*f.a[12]*g.a[7]+1llu*f.a[13]*g.a[10];
res.a[11]=1llu*f.a[11]*g.a[5]+1llu*f.a[13]*g.a[11];
res.a[12]=1llu*f.a[12]*g.a[8]+1llu*f.a[13]*g.a[12];
res.a[13]=1llu*f.a[13]*g.a[13];
return res;
}
void push_up(int cur) {
val[cur].a[0]=val[ls].a[0]+val[rs].a[0];
val[cur].a[1]=val[ls].a[1]+val[rs].a[1];
val[cur].a[2]=val[ls].a[2]+val[rs].a[2];
val[cur].a[3]=val[ls].a[3]+val[rs].a[3];
val[cur].a[4]=val[ls].a[4]+val[rs].a[4];
}
void build(int cur,int l,int r) {
val[cur].clr(); tag[cur].clr1();
val[cur].a[4]=r-l+1;
if(l==r) return ;
int mid=(l+r)>>1;
build(ls,l,mid); build(rs,mid+1,r);
push_up(cur);
}
void push_down(int cur) {
tag[ls]=mul(tag[ls],tag[cur]);
tag[rs]=mul(tag[rs],tag[cur]);
val[ls]=Mul(val[ls],tag[cur]);
val[rs]=Mul(val[rs],tag[cur]);
tag[cur].clr1();
}
void upt(int cur,int l,int r,int cl,int cr) {
if(cl<=l&&r<=cr) {
val[cur]=Mul(val[cur],P);
tag[cur]=mul(tag[cur],P);
return ;
}
int mid=(l+r)>>1;
push_down(cur);
if(cl<=mid) upt(ls,l,mid,cl,cr);
if(cr>mid) upt(rs,mid+1,r,cl,cr);
push_up(cur);
}
ull qry(int cur,int l,int r,int cl,int cr) {
if(cl<=l&&r<=cr) {
return val[cur].a[0];
}
int mid=(l+r)>>1;
push_down(cur);
if(cr<=mid) return qry(ls,l,mid,cl,cr);
if(cl>mid) return qry(rs,mid+1,r,cl,cr);
return qry(ls,l,mid,cl,cr)+qry(rs,mid+1,r,cl,cr);
}
signed main() {
cin.tie(0); ios::sync_with_stdio(false);
cin>>n; cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
for(int i=1;i<=n;i++) {
while(tp&&a[st[tp]]<a[i]) {
--tp;
}
if(tp) La[i]=st[tp];
st[++tp]=i;
}
tp=0;
for(int i=1;i<=n;i++) {
while(tp&&b[st[tp]]<b[i]) {
--tp;
}
if(tp) Lb[i]=st[tp];
st[++tp]=i;
}
cin>>q;
for(int i=1;i<=q;i++) {
int l,r; cin>>l>>r;
vec[r].pb(make_pair(l,i));
}
build(1,1,n);
for(int i=1;i<=n;i++) {
P.clr();
P.a[0]=1; P.a[7]=a[i]; P.a[8]=1; P.a[11]=a[i]; P.a[13]=1;
upt(1,1,n,La[i]+1,i);
P.clr();
P.a[0]=1; P.a[4]=b[i]; P.a[5]=1; P.a[12]=b[i]; P.a[13]=1;
upt(1,1,n,Lb[i]+1,i);
P.clr1(); P.a[1]=1;
upt(1,1,n,1,i);
for(auto x:vec[i]) ans[x.second]+=qry(1,1,n,x.first,i);
}
for(int i=1;i<=q;i++) cout<<ans[i]<<'\n';
return 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】