CF601B Solution
题解
可以发现,只有两相邻元素之间的差会对答案做出贡献。简单证明:设\(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\n",ans);
}
return 0;
}