考虑分治
将原序列拆成若干新区间,求经过 \(mid\) 的询问区间
在一段处理区间内,另开 \(4\) 棵线段树分别维护四段(根据最大值最小值划分)的系数
合并询问区间即可
注意如果某一个询问区间恰好包含该分治区间
直接后续更新答案即可,不然空间就爆成 \(n^2\) 了
这道题主要学的分治思想
Code
#include <bits/stdc++.h>
#define re register
#define db double
#define int long long
// #define ll long long
#define pir make_pair
using namespace std;
const int N=2e6+10;
const int maxn=4E5+10;
const int INF=1e18+10;
const int mol=1E9+7;
inline int read(){
int x=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9') w=(ch=='-')?-1:1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*w;
}
int n,m,a[maxn],ansl[maxn],maxl[maxn],minl[maxn],f[maxn];
struct QUS { int l,r,id; };
vector<QUS>qus[maxn<<2];
#define lid (id<<1)
#define rid (id<<1|1)
inline void ad(int &x) { x= x>=mol? x-mol:x; }
inline int ad1(int x) { return x>=mol? x-mol:x; }
struct STG {
int num[maxn];
struct TREE { int lazy,sum,ls; } tre[maxn<<2];
inline void init(int id,int l,int r) {
tre[id].lazy=tre[id].sum=0;
if(l==r) { tre[id].ls=num[l]; return ; }
int mid=(l+r)>>1;
init(lid,l,mid); init(rid,mid+1,r);
ad(tre[id].ls=tre[lid].ls+tre[rid].ls);
}
inline void push_down(int id) {
int s=tre[id].lazy; tre[id].lazy=0;
ad(tre[lid].sum+=tre[lid].ls*s%mol); ad(tre[rid].sum+=tre[rid].ls*s%mol);
ad(tre[lid].lazy+=s); ad(tre[rid].lazy+=s);
}
inline void insert(int id,int l,int r,int ll,int rr,int val) {
if(ll<=l&&r<=rr) { ad(tre[id].sum+=tre[id].ls*val%mol); ad(tre[id].lazy+=val); return ; }
if(tre[id].lazy) push_down(id);
int mid=(l+r)>>1;
if(ll<=mid) insert(lid,l,mid,ll,rr,val); if(rr>mid) insert(rid,mid+1,r,ll,rr,val);
ad(tre[id].sum=tre[lid].sum+tre[rid].sum);
}
inline int query(int id,int l,int r,int ll,int rr) {
if(ll<=l&&r<=rr) return tre[id].sum;
if(tre[id].lazy) push_down(id);
int mid=(l+r)>>1,ans=0;
if(ll<=mid) ad(ans+=query(lid,l,mid,ll,rr)); if(rr>mid) ad(ans+=query(rid,mid+1,r,ll,rr));
return ans;
}
} tree[4];
inline void slove(int id,int l,int r) {
if(l==r) {
f[id]=a[l]*a[l]%mol;
for(re int i=0;i<qus[id].size();i++) ad(ansl[qus[id][i].id]+=a[l]*a[l]%mol);
return ;
}
vector<int>vec;
int mid=(l+r)>>1;
sort(qus[id].begin(),qus[id].end(),[](QUS a,QUS b){ return a.r<b.r; });
int fg=0;
while(fg<qus[id].size()&&qus[id][fg].r<mid+1) ++fg;
maxl[mid]=a[mid]; minl[mid]=a[mid];
for(re int i=mid-1;i>=l;i--) {
maxl[i]=max(a[i],maxl[i+1]); minl[i]=min(a[i],minl[i+1]);
}
for(re int i=l;i<=mid;i++) {
tree[0].num[i]=1; tree[1].num[i]=maxl[i]; tree[2].num[i]=minl[i]; tree[3].num[i]=maxl[i]*minl[i]%mol;
}
for(re int i=0;i<4;i++) tree[i].init(1,l,mid);
int mi=INF,mx=-INF,x=mid+1,y=mid+1,z=mid+1;
for(re int i=mid+1;i<=r;i++) {
mi=min(mi,a[i]); mx=max(mx,a[i]);
while(x>l&&minl[x-1]>=mi&&maxl[x-1]<=mx) --x;
while(y>l&&minl[y-1]>=mi) --y;
while(z>l&&maxl[z-1]<=mx) --z;
int ls=0;
if(x<=mid) tree[0].insert(1,l,mid,x,mid,mi*mx%mol);
if(y<x) {
tree[1].insert(1,l,mid,y,x-1,mi);
if(y>l) tree[3].insert(1,l,mid,l,y-1,1);
}
if(z<x) {
tree[2].insert(1,l,mid,z,x-1,mx);
if(z>l) tree[3].insert(1,l,mid,l,z-1,1);
}
while(fg<qus[id].size()&&qus[id][fg].r==i) {
if(qus[id][fg].l<l||qus[id][fg].l>mid||(qus[id][fg].l==l&&qus[id][fg].r==r)) { ++fg; continue; }
for(re int j=0;j<4;j++) ad(ansl[qus[id][fg].id]+=tree[j].query(1,l,mid,qus[id][fg].l,mid));
++fg;
}
}
for(re int i=0,ll,rr;i<qus[id].size();i++) {
ll=qus[id][i].l; rr=qus[id][i].r;
if(ll==l&&rr==r) { vec.push_back(qus[id][i].id); continue; }
if(ll>mid) qus[rid].push_back(qus[id][i]);
else if(rr<=mid) qus[lid].push_back(qus[id][i]);
else {
qus[lid].push_back((QUS){ ll,mid,qus[id][i].id });
qus[rid].push_back((QUS){ mid+1,rr,qus[id][i].id });
}
}
for(re int i=0;i<4;i++) ad(f[id]+=tree[i].tre[1].sum);
slove(lid,l,mid); slove(rid,mid+1,r);
ad(f[id]+=ad1(f[lid]+f[rid]));
for(re int i=0;i<vec.size();i++) {
int ls=vec[i];
ad(ansl[ls]+=f[id]);
}
}
signed main(void) {
// freopen("erp.in","r",stdin); freopen("erp.out","w",stdout);
freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout);
n=read(),m=read();
for(re int i=1;i<=n;i++) a[i]=read();
for(re int i=1,l,r;i<=m;i++) {
l=read(),r=read(); qus[1].push_back((QUS){ l,r,i });
}
slove(1,1,n);
for(re int i=1;i<=m;i++) printf("%lld\n",ansl[i]);
return 0;
}