题意
给定一个数组a[1..n],求一个连续序列[l,r],要求a[l..r]的最小值乘以sigma(a[l..r])最大。输出最大值和区间。
解题
枚举区间最小值h=a[i]。
从i开始向左开始遍历求出第一个值比a[i]小的值的下标L[i]。
从i开始向右开始遍历求出第一个值比a[i]小的值的下标R[i]。
那么,最小值为a[i]的区间就是[L[i]+1,R[i]-1]。
用一个前缀和sum[i]来表示sigma(a[1..i]).枚举比较出最大值即可。
求L[i]和R[i]是单调栈的经典应用。
具体是维护一个单调递增的栈。
遍历a[1..n],如果a[i]比栈顶代表的值大,那么L[i]=栈顶。(栈里存的是下标)
AC代码
#include <cstdio>
#include <algorithm>
#include <stack>
#include <cstring>
using namespace std;
typedef long long ll;
const ll maxn=1e5+7;
ll sum[maxn];
ll a[maxn],n;
ll L[maxn];
void getL()
{
stack<ll> S;
for(ll i=1;i<=n;i++)
{
while(S.size() && a[S.top()]>=a[i]) S.pop();
L[i]=S.empty()?0:S.top();
// printf("%d %d\n",i,L[i]);
S.push(i);
}
}
ll R[maxn];
void getR()
{
stack<ll> S;
for(ll i=n;i>=1;i--)
{
while(S.size() && a[S.top()]>=a[i]) S.pop();
R[i]=S.empty()?n+1:S.top();
// printf("%d %d\n",i,R[i]);
S.push(i);
}
}
int main()
{
while(~scanf("%lld",&n))
{
sum[0]=0;
for(ll i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
sum[i]=sum[i-1]+a[i];
}
getL();
getR();
ll l=0,r=0;
ll ans=-1,tmp;
for(ll i=1;i<=n;i++)
{
tmp=a[i]*(sum[R[i]-1]-sum[L[i]]);
// printf("tmp=%lld\n",tmp);
if(tmp>ans)
{
ans=tmp;
l=L[i]+1;
r=R[i]-1;
}
}
printf("%lld\n%lld %lld\n",ans,l,r);
}
return 0;
}
/*
6
1000000 10021 100000 2255 12 1111111
*/