题意: 去掉题干,意思是在一组数里,找到一个以他自己为最小值的范围(向他两边扩展)里,用他的值(最小值)乘以这个范围内数之和。求最小值乘以范围数之和最大化,第一行输出这个最大化的值,第二行输出范围的左右位置。
思路:在做过poj在一片长度不一的矩形里求一个以最矮矩形扩展宽度并乘以宽度得到积的最大化的问题,类比这个我们同样利用构造单调递增栈求出每个点能向一个方向扩展的位置,不同的是这题需要记载这个区域的和,我就用sum数组记载(两个sum数组表示从两个方向扩展的累加和,最后合并,写的很烂……)扩展区域的和,注意,这里遇到前面栈里比自己高的点可以扩展时,sum加上是这个点已经确认sum,而不是只是这个点的权值,因为,前面可以扩展的点可能已出栈,而结果就记录在前面sum里。
#include<cstdio>
#include<stack>
#include<cstring>
using namespace std;
struct Node
{
long long v;int l,r;
}node[100010];
stack<int> s;
long long sum[100010],sum2[100010],n;
int main()
{
while(~scanf("%d",&n))
{
memset(sum,0,sizeof(sum));
memset(sum2,0,sizeof(sum2));
for(int i=1;i<=n;i++) { scanf("%lld",&node[i].v);sum[i]=sum2[i]=node[i].v;}
node[0].v=node[n+1].v=-1;
while(!s.empty()) s.pop();s.push(0);
for(int i=1;i<=n;i++)
{
int x=s.top();
while(node[x].v>=node[i].v)
{
sum[i]+=sum[x];
s.pop();
x=s.top();
}
node[i].l=x+1;
s.push(i);
}
while(!s.empty()) s.pop();s.push(n+1);
for(int i=n;i>0;i--)
{
int x=s.top();
while(node[x].v>=node[i].v)
{
sum2[i]+=sum2[x];
s.pop();
x=s.top();
}
node[i].r=x-1;
s.push(i);
}
for(int i=1;i<=n;i++)
sum[i]+=sum2[i]-node[i].v;
long long maxx=-1;
int L,R;
for(int i=1;i<=n;i++)
{
long long ans=node[i].v*sum[i];
if(ans>maxx)
{
maxx=ans;
L=node[i].l,R=node[i].r;
}
}
printf("%lld\n%d %d",maxx,L,R);
}
}