题意: 给你n个数,定义区间价值为区间最小值*区间和,求区间价值最大的区间
之前这个题在石油大oj上碰到过,当时想着用线段树做,记录区间最小值,但是寻找区间l和r的过程还是O(N^2),不会做就放弃了
时隔一两个月再次碰到这道题,就想着一定搞会,一搜题解是单调栈,正好单调栈还没学,就顺便学学,单调栈还挺有意思的。
思路:利用单调栈,维护这个栈是从栈顶到栈底是单调递增的,找出a[i] 左边最远能到的下标l 右边最远能到的r(这里的最远是指 l,r a[i]仍最小)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<stack>
#include<string.h>
#include<queue>
#include<cmath>
#define ll long long
#define inf 0x3f3f3f
using namespace std;
const int maxn=1e5+5;
ll n,a[maxn],Right[maxn],Left[maxn],sum[maxn];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
Left[i]=i;
Right[i]=i;
sum[i]=sum[i-1]+a[i];
}
stack<ll> s;
for(int i=1;i<=n;i++)
{
while(s.size()&&a[s.top()]>=a[i])
{
Left[i]=Left[s.top()];
s.pop();
}
s.push(i);
}
while(!s.empty())
s.pop();
for(int i=n;i>=1;i--)
{
while(s.size()&&a[s.top()]>=a[i])
{
Right[i]=Right[s.top()];
s.pop();
}
s.push(i);
}
ll ans=-1,ansl,ansr;
for(int i=1;i<=n;i++)
{
ll now=sum[Right[i]]-sum[Left[i]-1];
if(now*a[i]>ans)
{
ans=now*a[i];
ansl=Left[i];
ansr=Right[i];
}
}
cout<<ans<<endl<<ansl<<" "<<ansr;
return 0;
}