Description
给定长度为n的序列:a1,a2,…,an,记为a[1:n]。类似地,a[l:r](1≤l≤r≤N)是指序列:al,al+1,…,ar-
1,ar。若1≤l≤s≤t≤r≤n,则称a[s:t]是a[l:r]的子序列。现在有q个询问,每个询问给定两个数l和r,1≤l≤r
≤n,求a[l:r]的不同子序列的最小值之和。例如,给定序列5,2,4,1,3,询问给定的两个数为1和3,那么a[1:3]有
6个子序列a[1:1],a[2:2],a[3:3],a[1:2],a[2:3],a[1:3],这6个子序列的最小值之和为5+2+4+2+2+2=17。
Input
输入文件的第一行包含两个整数n和q,分别代表序列长度和询问数。接下来一行,包含n个整数,以空格隔开
,第i个整数为ai,即序列第i个元素的值。接下来q行,每行包含两个整数l和r,代表一次询问。
Output
对于每次询问,输出一行,代表询问的答案。
Sample Input
5 2 4 1 3
1 5
1 3
2 4
3 5
2 5
Sample Output
17
11
11
17
HINT
1 ≤N,Q ≤ 100000,|Ai| ≤ 10^9
题解:
考虑莫队,因为n比较大,我们需要O(1)转移.
假设当前区间是(l,r)我们要转移到(l,r+1),那多出来的只是r+1产生的贡献.
可以找到(l,r)中最靠右的最小值的位置t,那t左边到r+1的最小值都是a[t].
考虑t右边的数,我们可以预处理一个前缀和s,s[i]表示以i为右端点的子序列的贡献,
那t右边的数产生的贡献就是s[r+1]-s[t];其他移动更新方法同理.
使用st表可以O(1)查询最小值.
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 100010
using namespace std;
int a[N],st[N],l[N],r[N],bl[N],cnt,f[20][N],n,m,bk;
long long ans[N],sp[N],sa[N],temp;
struct use{int l,r,id;}q[N];
void pre(){
int top;
top=1;st[top]=1;st[0]=0;
for (int i=2;i<=n;i++){
while (top&&a[st[top]]>a[i]) top--;
l[i]=st[top];st[++top]=i;
}
top=1;st[top]=n;st[0]=n+1;
for (int i=n-1;i>=1;i--){
while (top&&a[st[top]]>a[i]) top--;
r[i]=st[top];st[++top]=i;
}
sp[1]=a[1];
for (int i=2;i<=n;i++)
sp[i]=sp[l[i]]+(long long)(i-l[i])*a[i];
sa[n]=a[n];
for (int i=n-1;i>=1;i--)
sa[i]=sa[r[i]]+(long long)(r[i]-i)*a[i];
for (int i=1;i<=n;i++) f[0][i]=i;
for (int j=1;j<=18;j++)
for (int i=1;i<=n;i++){
if (a[f[j-1][i]]>=a[f[j-1][i+(1<<j>>1)]])
f[j][i]=f[j-1][i+(1<<j>>1)];
else f[j][i]=f[j-1][i];
}
}
bool cmp(use a,use b){
if (bl[a.l]==bl[b.l]) return a.r<b.r;
else return a.l<b.l;
}
int query(int l,int r){
int t=log2(r-l+1);
int x=f[t][l],y=f[t][r-(1<<t)+1];
if (a[x]<a[y]) return x;
else return y;
}
void dell(int l,int r){
if (l>r) return;
int t=query(l,r);
temp-=(long long)(r-t+1)*a[t];
temp-=sa[l]-sa[t];
}
void addl(int l,int r){
if (l>r) return;
int t=query(l,r);
temp+=(long long)(r-t+1)*a[t];
temp+=sa[l]-sa[t];
}
void delr(int l,int r){
if (l>r) return;
int t=query(l,r);
temp-=(long long)(t-l+1)*a[t];
temp-=sp[r]-sp[t];
}
void addr(int l,int r){
if (l>r) return;
int t=query(l,r);
temp+=(long long)(t-l+1)*a[t];
temp+=sp[r]-sp[t];
}
void solve(){
int l=0,r=0;temp=0;
for (int i=1;i<=m;i++){
while (l<q[i].l){dell(l,r);l++;}
while (l>q[i].l){l--;addl(l,r);}
while (r<q[i].r){r++;addr(l,r);}
while (r>q[i].r){delr(l,r);r--;}
ans[q[i].id]=temp;
}
}
int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
pre();
for (int i=1;i<=m;i++){
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}
bk=sqrt(n);cnt=n/bk+(n%bk!=0);
for (int i=1;i<=n;i++) bl[i]=(i-1)/bk+1;
sort(q+1,q+m+1,cmp);
solve();
for (int i=1;i<=m;i++){
printf("%lld",ans[i]);
if (i!=m) puts("");
}
}