Description
Input
Output
Sample Input
6 3 1 6 4 5 2
Sample Output
60 3 5
Source
通过这道题可以了解单调栈的一种功能。
这道题有两个限制条件:要求区间的总和,而都是正数,当然希望区间越长越好;但是还有最小值的限制,所以我们可以想到以一个点为中心,假定这个点是区间的最小值,然后向两边扩展,求出这个区间(当然是要最长的)。即f[i].l,f[i].r分别表示以a[i]为最小值的最长区间的左端点和右端点(也就是说最多到哪里元素都比a[i]大)。
元素进入单调栈的时候,先把栈内所有的大于它的元素都弹出,然后再将它压入(维护由栈顶向栈底递减的单调栈)。由单调栈的单调性可知,元素出栈时一定比将要进栈的元素大,将要进栈的元素一定比将要出栈的元素小,出栈后栈顶的元素一定比刚刚出栈的元素小(但不一定要比马上要入栈的元素小,还有可能要继续出栈),且栈底元素的序号一定比栈顶元素小。所以但凡遇到元素出栈(在操作过程中元素出栈往往由新元素入栈引起),将要进栈的元素的l向左延伸,出栈后栈顶元素的r向右延伸,且完全出栈后(栈顶所有比当前元素大的元素都已弹出),栈顶元素的r向右延伸。
注意最后要把栈里的元素全部弹出,并且也要进行上面的操作。
之后枚举每一个f,用前缀和求出区间和,更新答案即可。
【代码】
#include
#include
#include
#define ll long long
using namespace std;
struct hp{
ll l,r;
}f[100005];
int n,ansl,ansr,tmp;
ll sum,Max;
ll a[100005],strack[100005];
ll s[100005];
inline void push(ll x){
if (x==1){
strack[++tmp]=x;
return;
}
while (a[strack[tmp]]>=a[x]&&tmp>0){
f[x].l=f[strack[tmp]].l;//将要进栈的l向左延伸
f[strack[tmp-1]].r=f[strack[tmp]].r;//出栈后栈顶元素的r向右延伸
--tmp;
}
f[strack[tmp]].r=f[x].r;//完全出栈后栈顶元素的r向右延伸
strack[++tmp]=x;
}
inline void pop(){
f[strack[tmp-1]].r=f[strack[tmp]].r;
tmp--;
}
int main(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%d",&a[i]),s[i]=s[i-1]+a[i],f[i].l=f[i].r=i;
for (int i=1;i<=n;++i)
push(i);
while (tmp>0) pop();
for (int i=1;i<=n;++i){
sum=s[f[i].r]-s[f[i].l-1];
if (Max<=sum*a[i]){
Max=sum*a[i];
ansl=f[i].l,ansr=f[i].r;
}
}
printf("%I64d\n%d %d\n",Max,ansl,ansr);
}