luogu P2422 良好的感觉 单调栈

题目描述

kkk做了一个人体感觉分析器。每一天,人都有一个感受值Ai,Ai越大,表示人感觉越舒适。在一段时间[i, j]内,人的舒适程度定义为[i, j]中最不舒服的那一天的感受值 * [i, j]中每一天感受值的和。现在给出kkk在连续N天中的感受值,请问,在哪一段时间,kkk感觉最舒适?

输入输出格式

输入格式:

 

第一行为N,代表数据记录的天数

第二行N个整数,代表每一天的感受值

 

输出格式:

 

一行,表示在最舒适的一段时间中的感受值。

题解: 

看上去无从下手,不妨枚举那个最不舒服的感受值(即一段区间中最小值).
令 $f_{i}$ 表示 $i$ 位置为最小值所能扩展的最大区间的感受值之和.
用一个单调递增的队列维护最小感受值.
考虑新加入一个元素 $a_{i}$.
如果当前队首大于 $a_{i}$,那么 $i$ 就是队首能扩展到的最大位置了,弹出队首,把队首的 $f_{j}$ 值加上 $sum(i-1)-sum(j)$,一直反复弹栈操作.
最后,将 $a_{i}$ 推进.
因为栈时单调递增的,$a_{i}$ 再栈中前一个元素肯定小于等于 $a_{i}$,所以当前的贡献就是 $f_{i}=sum(i)-sum(q_{top})$.
但是.....1. 多个元素大小相等怎么办 ? 2. 到最后都弹不掉怎么办 ?
在最后加入一个 0,即可解决上述问题. 
#include<bits/stdc++.h>
#define maxn 1000000 
#define ll long long 
using namespace std;
void setIO(string s)
{
	string in=s+".in"; 
	freopen(in.c_str(),"r",stdin);    
}
int S[maxn],top,n; 
ll f[maxn],sumv[maxn],arr[maxn];  
int main()
{
	// setIO("input"); 
	S[++top]=0;     
	scanf("%d",&n); 
	for(int i=1;i<=n;++i)
	{
		scanf("%lld",&arr[i]); 
		sumv[i]=sumv[i-1]+arr[i]; 
		while(arr[S[top]] > arr[i]) 
		{
			f[S[top]] += sumv[i-1]-sumv[S[top]];          
			--top; 
		}
		f[i]=sumv[i]-sumv[S[top]];     
		S[++top]=i;       
	}
	ll ans=0; 
	for(int i=1;i<=n;++i) ans=max(ans,f[i]*arr[i]); 
	printf("%lld\n",ans); 
    return 0; 
}

  

posted @ 2019-06-18 19:49  EM-LGH  阅读(190)  评论(0编辑  收藏  举报