非常容易想到单调队列维护最小值
非常容易想到莫队进行区间之间的转换
非常不容易找到转换的方式(本人认为)
在看了几个版本的题解之后 发现讲的都不是特别易懂
(主要是你看不懂)
所以我就自己重新写一个吧
假设现在已经知道了
[L,R]
的结果
那么这个时候推
[L,R+1]
的答案
首先 用 [L,R+1] 的最小值坐标 P (注意是坐标) 将原区间分为两段
如下图
(蓝色代表原区间 黑色是
原本的所有区间的最小值和就在 [L,R] 的结果中
那么 [L,R+1] 相比于 [L,R] 新加入的的区间就是 左端点在 [L,R+1] 右端点为 R+1 的所有区间
对于左端点在
[L,P]
区间最小值一定为
P
点的值
也就是说说左端点在红色的那一端的所有的区间的最小值都是
那么统计下来的和就是
(P−L+1)∗P
(此处的P为P点的值)
那么对于左端点在 [P+1,R] 的区间之和怎么算呢??
重点来了
引入一个新的值
Sum[x]
表示的是 以
x
为右端点 的所有区间的最小值之和
这其实是一个前缀和
推导
1、找到
x
左边的第一个小于
2、我们是从左到右推导的 所以
3、对于 左端点在
[1,p]
右端点为
x
的区间 因为最小值都是小于等于
4、而对于 左端点在
[p+1,x]
右端点为
x
的区间 最小值即为
所以
Sum[x]=Sum[p]+(x−p+1)∗X
(
X
为 点
注意第3、4点 我们根据这些操作的内涵 就可以得出一个结论
对于点
x
若
反过来说
Sum[x]−Sum[p]
就是 左端点在
[p+1,x]
右端点为
x
的区间最小值的和
所以说 区间结果
反之 Res[L,R]−>Res[L+1,R] 的公式(此处的 Sum[x] 为 以 x 为左端点的所有区间最小值之和)
代码我…并没有写 什么时候有空写一下吧
代码
#include <iostream>
#include <cmath>
#include <cstdio>
#include <stack>
#include <algorithm>
using namespace std;
inline int input()
{
char c=getchar();int o;bool f=0;
while(c>57||c<48)f|=(c=='-'),c=getchar();
for(o=0;c>47&&c<58;c=getchar())o=(o<<1)+(o<<3)+c-48;
return f?-o:o;
}
int size,BL[100123];
struct que
{
int l,r,i;
bool operator <(que& b)const{return BL[l]==BL[b.l]?r<b.r:BL[l]<BL[b.l];}
}Q[100123];
int n,q,A[100123],LG[100123],SP[100123][22];
long long LS[100123],RS[100123];
long long res=0,RES[100123];
inline int minp(int a,int b){return A[a]<A[b]?a:b;}
inline int ASK(int l,int r)
{
if(l>r)swap(l,r);
int len=r-l+1,p=-1,nlen;
p=LG[len];
nlen=1<<p;
return minp(SP[l][p],SP[r-nlen+1][p]);
}
inline void Rch(int l,int r,long long op)
{
int p=ASK(l,r);
res+=op*(1LL*(p-l+1)*A[p]+LS[r]-LS[p]);
}
inline void Lch(int l,int r,long long op)
{
int p=ASK(l,r);
res+=op*(1LL*(r-p+1)*A[p]+RS[l]-RS[p]);
}
void work()
{
sort(Q+1,Q+q+1);
int L,R,l=1,r=1;res=A[1];
for(int i=1;i<=q;i++)
{
L=Q[i].l;R=Q[i].r;
while(r<R)Rch(l,++r,1);
while(l>L)Lch(--l,r,1);
while(r>R)Rch(l,r--,-1);
while(l<L)Lch(l++,r,-1);
RES[Q[i].i]=res;
}
for(int i=1;i<=q;i++)printf("%lld\n",RES[i]);
}
int go[100123],p=0;;
void init()
{
int flr=LG[n];
for(int i=1;i<=n;i++)SP[i][0]=i;
for(int i=1,l=1;i<=flr;i++,l<<=1)
for(int p=1;p<=n;p++)
{
SP[p][i]=SP[p][i-1];
if(p+l<=n)SP[p][i]=minp(SP[p][i-1],SP[p+l][i-1]);
}
go[p=1]=0;
int l,r;
for(int i=1;i<=n;i++)
{
while(A[go[p]]>A[i])p--;
l=go[p];go[++p]=i;
LS[i]=LS[l]+1LL*(i-l)*A[i];
}
go[p=1]=n+1;
for(int i=n;i;i--)
{
while(A[go[p]]>=A[i])p--;
r=go[p];go[++p]=i;
RS[i]=RS[r]+1LL*(r-i)*A[i];
}
}
int main()
{
freopen("In.txt","r",stdin);
freopen("Out.txt","w",stdout);
n=input();q=input();
size=sqrt(n);
for(int i=2;i<=n;i++)LG[i]=LG[i>>1]+1;
for(int i=size;i<=n;i++)BL[i]=BL[i-size]+1;
A[0]=A[n+1]=-(1000000010);
for(int i=1;i<=n;i++)A[i]=input();
init();
for(int i=1;i<=q;i++)Q[i].i=i,Q[i].l=input(),Q[i].r=input();
work();
}