(n1e5)
求区间,莫队??
确实可以,时间复杂度(O(nlogn+sqrt n)),但DP只用(O(nlogn))
SOL:
(f[l][r])表示右端点为(r),左端为([l,r])的答案
(pre_x)表示x位置前第一个小于(a_x)是位置
(f[l][r]=f[l][pre_r]+a_r*(r-pre_r))
只要我们使得存在(pre_x=l-1),那么可以去掉第一维
[f_{r}=a_r*(r-pre_r)+…+a_x*(x-l+1)+f[l-1]
]
[f[l][r]=f[r]-f[l-1]
]
我们只需要在([l,r])中找到最小值的位置p,单独算跨过p的区间,就分为两个可以用上述方法解决的子问题(左右各DP一次)
但是这样明显过不去(在此基础上莫队可以卡过),需要优化
用前缀和优化(s_i=sum_{j=1}^if_j)
[ans=s_r-s_p-f_p*(r-p)
]
时间复杂度(O(nlogn))
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f==1?x:-x;
}
const int N=1e5+4,inf=1e9+7;
int n,m,top,a[N],lg[N],mn[N][20],fl[N],sl[N],fr[N],sr[N],st[N],pre[N],suf[N];
inline int cmp(int x,int y){return a[x]<a[y]?x:y;}
inline int ask(int x,int y){
int l=lg[y-x+1];
return cmp(mn[x][l],mn[y-(1<<l)+1][l]);
}
signed main(){
n=read();m=read();a[0]=inf;a[n+1]=-inf;
for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
for(int i=1;i<=n;i++){a[i]=read();mn[i][0]=i;}
for(int j=1;j<=lg[n];j++)
for(int i=1,mx=n-(1<<j)+1;i<=mx;i++)//
mn[i][j]=cmp(mn[i][j-1],mn[i+(1<<j-1)][j-1]);
for(int i=1;i<=n+1;i++){
while(top&&a[st[top]]>a[i])suf[st[top--]]=i;
pre[i]=st[top];st[++top]=i;
}
for(int i=1;i<=n;i++){
fr[i]=a[i]*(i-pre[i])+fr[pre[i]];
sr[i]=sr[i-1]+fr[i];
}
for(int i=n;i;i--){
fl[i]=a[i]*(suf[i]-i)+fl[suf[i]];
sl[i]=sl[i+1]+fl[i];
}
while(m--){
static int l,r,p;
l=read();r=read();p=ask(l,r);
cout<<(p-l+1)*(r-p+1)*a[p]+
sr[r]-sr[p]-fr[p]*(r-p)+
sl[l]-sl[p]-fl[p]*(p-l)<<"
";
}
return (0-0);
}