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 5
5 2 4 1 3
1 5
1 3
2 4
3 5
2 5
Sample Output
28
17
11
11
17
HINT
1 ≤N,Q ≤ 100000,|Ai| ≤ 10^9
分析:
容易想到可以对一端进行插入,我们设f[i]f[i]f[i]表示 l=il=il=i,r=[i,n]r=[i,n]r=[i,n]的区间[l,r][l,r][l,r]的权值和。
考虑左端插入一位,也就是求l=xl=xl=x,r=[x,y]r=[x,y]r=[x,y]的区间[l,r][l,r][l,r]权值和。
假设ddd是[x,y][x,y][x,y]中最小值的位置,那么对于一个区间[x,k][x,k][x,k]的权值一定等于区间[d,k][d,k][d,k]的权值。
所以f[x]−f[d]f[x]-f[d]f[x]−f[d]就是l=xl=xl=x,r=[x,d−1]r=[x,d-1]r=[x,d−1]的区间[l,r][l,r][l,r]权值和,而跨越ddd的区间权值一定为ddd,我们就求出了在左端插入增加的价值,减少的价值同理。
在另一边同理维护ggg,就可以满足右端插入了,然后就可以莫队解决了。
复杂度是O(nn)O(n\sqrt{n})O(nn)的,预处理f,gf,gf,g是O(n)O(n)O(n)的,最小值可以rmq。
代码:
/**************************************************************
Problem: 4540
User: ypxrain
Language: C++
Result: Accepted
Time:7096 ms
Memory:13420 kb
****************************************************************/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define LL long long
const int maxn=1e5+7;
using namespace std;
int n,m,top;
LL f[maxn],g[maxn],ans;
int a[maxn],bel[maxn],lg[maxn];
int r[maxn][18];
int h[maxn];
struct rec{
int num,l,r;
LL ans;
}q[maxn];
void prework()
{
for (int i=1;i<=n;i++)
{
h[++top]=i;
while ((top>1) && (a[h[top-1]]>a[h[top]])) h[--top]=h[top+1];
g[i]=g[h[top-1]]+((LL)i-(LL)h[top-1])*(LL)a[i];
}
top=0;
for (int i=n;i>0;i--)
{
h[++top]=i;
while ((top>1) && (a[h[top-1]]>a[h[top]])) h[--top]=h[top+1];
f[i]=f[h[top-1]]+((LL)h[top-1]-(LL)i)*(LL)a[i];
}
int block=trunc(sqrt(n));
for (int i=1;i<=n;i++) bel[i]=(i-1)/block+1;
for (int i=1;i<=n;i++) r[i][0]=i;
for (int j=1;j<18;j++)
{
for (int i=1;i<=n-(1<<j)+1;i++)
{
if (a[r[i][j-1]]<a[r[i+(1<<(j-1))][j-1]]) r[i][j]=r[i][j-1];
else r[i][j]=r[i+(1<<(j-1))][j-1];
}
}
for (int i=1;i<=n;i++) lg[i]=trunc(log(i+0.5)/log(2));
}
bool cmp1(rec a,rec b)
{
if (bel[a.l]==bel[b.l]) return a.r<b.r;
return a.l<b.l;
}
bool cmp2(rec a,rec b)
{
return a.num<b.num;
}
int getmin(int x,int y)
{
int k=lg[y-x+1];
int le=r[x][k],ri=r[y-(1<<k)+1][k];
if (a[le]<a[ri]) return le;
else return ri;
}
void ins1(int l,int r,int op)
{
int d=getmin(l,r);
LL s=g[r]-g[d]+((LL)d-(LL)l+1)*(LL)a[d];
ans+=op*s;
}
void ins2(int l,int r,int op)
{
int d=getmin(l,r);
LL s=f[l]-f[d]+((LL)r-(LL)d+1)*(LL)a[d];
ans+=op*s;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=1;i<=m;i++)
{
scanf("%d%d",&q[i].l,&q[i].r);
q[i].num=i;
}
prework();
sort(q+1,q+m+1,cmp1);
int l=1,r=1;
ans=a[1];
for (int i=1;i<=m;i++)
{
for (;r<q[i].r;r++) ins1(l,r+1,1);
for (;r>q[i].r;r--) ins1(l,r,-1);
for (;l>q[i].l;l--) ins2(l-1,r,1);
for (;l<q[i].l;l++) ins2(l,r,-1);
q[i].ans=ans;
}
sort(q+1,q+m+1,cmp2);
for (int i=1;i<=m;i++) printf("%lld\n",q[i].ans);
}