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
",ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/violetholmes/p/14641472.html