题意:求出以a[i]为区间最小数字的区间值(a[i]*区间和)
单调栈的性质:
1、维护从左往右的递增栈可以得到左边第一个比a[i]小的元素位置L[i]
2、维护从左往右的递减栈可以得到左边第一个比a[i]大的元素位置L[i]
3、维护从右往左的递增栈可以得到右边第一个比a[i]小的元素位置R[i]
4、维护从右往左的递减栈可以得到右边第一个比a[i]大的元素位置R[i]
因此,只需要先用前缀和存一下和,然后两个for循环从前往后维护递增栈和从后往前维护递增栈分区别找出左边第一个小的位置和右边第一小的位置,再找出他们的差(前缀和做差)*a[i]即可
l 1 1 3 3 5 3
a[i] 3 1 6 4 5 2
r 1 6 3 5 5 6
代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
using namespace std;
long long num[100003];
long long sum[100003];
int lef[100003];
int rig[100003];
int n;
int main(){
/* STL写法
stack<int> st;
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>num[i];
lef[i]=i;
rig[i]=i;
}
for(int i=1;i<=n;i++){
if(st.empty()||num[st.top()]<num[i])
st.push(i);
else{
lef[i]=lef[st.top()];
st.pop();
i--;
}
}
for(int i=n;i>=1;i--){
if(st.empty()||num[st.top()]<num[i])
st.push(i);
else{
rig[i]=rig[st.top()];
st.pop();
i++;
}
}*/
while(~scanf("%d",&n)){
for(int i=1;i<=n;i++){
cin>>num[i];
sum[i]=num[i]+sum[i-1];
lef[i]=rig[i]=i;
}
for(int i=2;i<=n;i++){
while(lef[i]>1&&num[lef[i]-1]>=num[i])
lef[i]=lef[lef[i]-1];
}
for(int i=n-1;i>=1;i--){
while(rig[i]<n&&num[rig[i]+1]>=num[i])
rig[i]=rig[rig[i]+1];
}
long long m=-1;
long long ans;
int l,r;
for(int i=1;i<=n;i++){
ans=num[i]*(sum[rig[i]]-sum[lef[i]-1]);
if(ans>m){
m=ans;
l=lef[i];
r=rig[i];
}
}
printf("%I64d\n%d %d",m,l,r);
}
return 0;
}