题目
比尔正在为人类情感开发一种新的数学理论。他最近的研究致力于研究好日子或坏日子如何影响人们对某个时期的记忆。
比尔最近开发的一个新想法为人类生活的每一天分配一个非负整数值。
比尔将此价值称为当天的情感价值。情绪值越大,日子就越好。比尔认为,人类生命中某个时期的价值与给定时期内各天的情感价值之和乘以该时期内最小的情感价值成正比。这种模式反映了一个非常糟糕的一天可能会大大破坏平均周期。
现在比尔正计划调查他自己的生活,找出他生命中最有价值的时期。帮助他这样做。
输入
输入的第一行包含 n - 他计划调查的比尔一生的天数(1 <= n <= 100 000)。文件的其余部分包含 n 个整数 a1, a2, ... a 范围从 0 到 10 6 - 当天的情绪值。数字由空格和/或换行符分隔。
输出
在第一行打印比尔一生中某个时期的最大价值。并在第二行打印两个数字 l 和 r,使得从 Bill 生命的第 l 天到第 r 天(含)的时间段具有最大可能的值。如果有多个具有最大可能值的周期,则打印其中的任何一个。
样本输入
6
3 1 6 4 5 2
样本输出
60
3 5
链接
题意
寻找一段区间,使的区间內最小值*区间元素和最大
思路
首先根据题意:最小值*元素和,来确定几个结论
1,满足条件的区间的右边界。一定是某次下降的前一个。因为右边界再向右边加上一个更大的,区间最小值不会小,元素和却一定大。
2,区间左边界同理,一定是某次上升的后一个。
3,找到右边界后向左判断,已经出栈的高元素不再考虑(即只判断在栈中的)。因为出栈是因为中间有更小的,那继续向左将增加元素和,不降低最小值
4,维护单调栈,最小值是每次的栈顶。因为每次判断的左边界是上一个更小栈的后一个,再由2可知该结论。
思路
先维护一个单调递增的栈(上升即入栈,下降即弹出之前再入栈),一旦遇到有下降的情况,将前一个作为右边界(即 i-1 ),向左开始判断,记录最小的ans即可
代码
#include <iostream>
#include <cstdio>
#include <stack>
using namespace std;
typedef long long ll;
#define M 100005
ll n,ans=-1,l,r,mid,last;
ll a[M],sum[M];
stack<int> s;
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
sum[i]=sum[i-1]+a[i]; //前缀和
}
a[++n]=-1; ///n将+1,确保最后一个元素很小,来将前面储存的栈进行判断
for(int i=1;i<=n;i++){
while(!s.empty()&&a[i]<a[s.top()]){
mid=s.top(); ///mid是区间(last+1,i-1)内最小的,即栈顶
s.pop();
if(s.empty()) last=0; ///last为下一个最小的,last+1即本次判断的左边界
else last=s.top();
if(ans<a[mid]*(sum[i-1]-sum[last])) ///本题核心是根据题意找到左右范围(last+1,i-1),以及维护单调栈获得最小值(mid),优化判断次数而已
{
ans=a[mid]*(sum[i-1]-sum[last]);
l=last+1;
r=i-1;
}
}
s.push(i);
}
cout<<ans<<endl<<l<<" "<<r<<endl;
return 0;
}