题解
可以发现,只有两相邻元素之间的差会对答案做出贡献。简单证明:设(i<k<j,a_i<a_j),若(a_k>a_j>a_i),
易得(a_k-a_i>a_j-a_i,k-i+1<j-i+1),所以(frac{a_k-a_i+a_k-a_j}{k-i+1}>frac{a_j-a_i}{j-i+1}),(a_k<a_i<a_j)同理;若(a_i<a_k<a_j),因为(a_k-a_i+a_j-a_k=a_j-a_i,k-i+1<j-i+1),所以(frac{a_k-a_i+a_j-a_k}{k-i+1}>frac{a_j-a_i}{j-i+1})。
设(b_i=|a_i-a_{i+1}|),对于每组询问,我们只需求出(b)数组([l,r])内子区间最大值之和。设(bf_i)为(b_i)左侧最后一个(>b_i)的元素,(af_i)为(b_i)右侧第一个(>b_i)的元素,(b_i)作为最大值的区间即为((bf_i,af_i)),可对答案产生贡献的区间数为((af_i-i)cdot (i-bf_i))。维护单调递减的单调栈,正反两边求出(bf,af)数组,即可在(O(1))时间内计算(b_i)对答案的贡献值。总时间复杂度(O(nq))。
AC代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
int a[N],b[N],bf[N],af[N],st[N],top;
signed main()
{
int n,q,l,r,ans;
scanf("%lld%lld",&n,&q);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<n;i++) b[i]=abs(a[i]-a[i+1]);
while(q--)
{
scanf("%lld%lld",&l,&r);
top=ans=0;
for(int i=l;i<r;i++)
{
while(top>0 && b[st[top]]<=b[i]) top--;
bf[i]=st[top]; st[++top]=i;
}
top=0;
for(int i=r-1;i>=l;i--)
{
while(top>0 && b[st[top]]<b[i]) top--;
af[i]=st[top]; st[++top]=i;
}
for(int i=l;i<r;i++)
{
if(!bf[i]) bf[i]=l-1;
if(!af[i]) af[i]=r;
}
for(int i=l;i<r;i++) ans+=b[i]*(i-bf[i])*(af[i]-i);
printf("%lld
",ans);
}
return 0;
}