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;
}
posted @ 2021-04-10 17:06  violet_holmes  阅读(40)  评论(0编辑  收藏  举报