6854. 【2020.11.04提高组模拟】古老的序列问题
一个数列,若干次询问,每次询问\([l,r]\)区间中所有子区间的\(max*min\)的和。
\(n,Q\le 10^5\)
老套路,维护个单调递增的栈和单调递减的栈,枚举右端点,维护每个左端点的贡献。搞个历史和,但是由于这里维护的是乘积,所以要维护各种各样的信息,比较复杂。反正我是没有写出来。
另一种做法:分治,将每个询问拆成\(O(\log n)\)个子问题,表示一个区间\([l,r]\)中,左端点在左半边,右端点在右半边的贡献。
对右区间搞个前缀\(max\)和\(min\),枚举左端点,求出左边的最大值和最小值,计算最值在右边的分界点(左边用左半边的最值,右边用右半边的前缀最值),这样就可以分成三个区间(实际上是四种区间)。对这四种区间分别用数据结构维护,支持区间加系数和区间询问和。直接树状数组即可。
时间\(O(n\lg^2 n)\)
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define N 100005
#define ll long long
#define mo 1000000007
#define INF 1000000000
int n,m;
ll s[N];
struct Query{int l,r,t;};
vector<Query> q[N*4];
bool cmpp(Query x,Query y){return x.l>y.l;}
ll f[N*4],ans[N];
struct TA{
int len;
ll t[N];
void init(int _n=n){
len=_n;
memset(t,0,sizeof(ll)*(len+1));
}
void add(int x,ll c){
if (!c) return;
for (;x<=len;x+=x&-x)
(t[x]+=c)%=mo;
}
ll query(int x){
ll res=0;
for (;x;x-=x&-x)
res+=t[x];
return res%mo;
}
};
struct TA2{
int len,offset;
TA t0,t1;
ll p[N];
// ll s[N];
void init(int _offset,int _n=n,ll *_p=NULL){
offset=_offset,len=_n;
t0.init(len),t1.init(len);
// if (_p==NULL) for (int i=1;i<=len;++i) p[i]=1;
// else for (int i=1;i<=len;++i) p[i]=_p[i+offset];
// memset(s,0,sizeof(ll)*(len+1));
if (_p==NULL) for (int i=1;i<=len;++i) p[i]=i;
else for (int i=1;i<=len;++i) p[i]=(p[i-1]+_p[i+offset])%mo;
}
void add(int l,int r,ll c){
if (l>r) return;
l-=offset,r-=offset;
// for (int i=l;i<=r;++i)
// (s[i]+=c)%=mo;
t1.add(l,c),t1.add(r+1,mo-c);
t0.add(l,mo-c*p[l-1]%mo);
t0.add(r+1,c*p[r]%mo);
}
ll query(int r){
r-=offset;
// ll res=0;
// for (int i=1;i<=r;++i)
// (res+=p[i]*s[i])%=mo;
// return res;
return (t1.query(r)*p[r]+t0.query(r))%mo;
}
} _,A,B,AB;
void divide(int k,int l,int r){
if (l==r){
f[k]=s[l]*s[l]%mo;
for (int i=0;i<q[k].size();++i)
(ans[q[k][i].t]+=f[k])%=mo;
q[k].clear();
return;
}
static vector<Query> p;
p.clear();
int mid=l+r>>1;
for (int i=0;i<q[k].size();++i)
if (q[k][i].l<=mid && mid<q[k][i].r && !(q[k][i].l==l && q[k][i].r==r))
p.push_back(q[k][i]);
sort(p.begin(),p.end(),cmpp);
static ll a[N],b[N],ab[N];
a[mid]=INF,b[mid]=-INF;
for (int i=mid+1;i<=r;++i){
a[i]=min(a[i-1],s[i]);
b[i]=max(b[i-1],s[i]);
ab[i]=a[i]*b[i]%mo;
}
int len=r-mid;
_.init(mid,len),A.init(mid,len,a),B.init(mid,len,b),AB.init(mid,len,ab);
int al=mid,bl=mid;
ll la=INF,lb=-INF;
for (int i=mid,j=0;i>=l;--i){
la=min(la,s[i]),lb=max(lb,s[i]);
for (;al<r && a[al+1]>la;++al);
for (;bl<r && b[bl+1]<lb;++bl);
_.add(mid+1,min(al,bl),la*lb%mo);
if (bl<al) B.add(min(al,bl)+1,al,la);
if (al<bl) A.add(min(al,bl)+1,bl,lb);
AB.add(max(al,bl)+1,r,1);
for (;j<p.size() && p[j].l==i;++j)
(ans[p[j].t]+=_.query(p[j].r)+A.query(p[j].r)+B.query(p[j].r)+AB.query(p[j].r))%=mo;
}
(f[k]+=_.query(r)+A.query(r)+B.query(r)+AB.query(r))%=mo;
p.clear();
for (int i=0;i<q[k].size();++i){
int L=q[k][i].l,R=q[k][i].r;
if (L==l && R==r) continue;
if (R<=mid)
q[k<<1].push_back(q[k][i]);
else if (L>mid)
q[k<<1|1].push_back(q[k][i]);
else{
q[k<<1].push_back((Query){L,mid,q[k][i].t});
q[k<<1|1].push_back((Query){mid+1,R,q[k][i].t});
}
}
divide(k<<1,l,mid);
divide(k<<1|1,mid+1,r);
f[k]+=f[k<<1]+f[k<<1|1];
for (int i=0;i<q[k].size();++i)
if (q[k][i].l==l && q[k][i].r==r)
(ans[q[k][i].t]+=f[k])%=mo;
q[k].clear();
// printf("f[%d]=%lld\n",k,f[k]);
}
int main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i)
scanf("%lld",&s[i]);
for (int i=1;i<=m;++i){
int l,r;
scanf("%d%d",&l,&r);
q[1].push_back((Query){l,r,i});
}
divide(1,1,n);
for (int i=1;i<=m;++i)
printf("%lld\n",ans[i]);
return 0;
}