"###Feel Good"

题面描述
问题描述

给出一个长度为n(1<=n<=100 000)的序列,求出一个子序列,使得这个序列中的最小值乘以这个序列的和的值最大。

输入格式

输入数据有2行,第一行正整数n,第2行n个正整数ai。(0<=ai<=10^6)

输出格式
输出有2行,第一行为题目描述中的最大值,第二行2个数字,分别为子序列的起点和终点位置,如果有多个子序列相等,那么选择起点靠前的。

样例输入
6
3 1 6 4 5 2
样例输出
60
3 5
限制与约定
时间限制:1s
空间限制:128MB

题解
我们开两个单调栈l[i]和r[i],从1~n 枚举 每个数,对于数 i,l[i]存 i点左边第一个比a[i]小的数的编号,r[i]存 i点右边第一个比a[i]小的数的编号,显然,l[i] 到 r[i] 这个子序列的最小值为a[i] 。因为序列为正整数序列,所以a[i] 为最小值的子序列能取到的最大值k为 l[i]~r[i] 的和乘上a[i] ,l[i]~r[i] 的和用前缀和处理出来,不断用k更新ans*(注意:ans要开 long long 类型)*,详见代码

代码实现

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdlib>
using namespace std;
long long a[100005],sum[100005];
int n,ll,rr,l[100005],r[100005];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        sum[i]=sum[i-1]+a[i];//前缀和处理
        l[i]=r[i]=i;
    }
    for(int i=2;i<=n;i++) while(l[i]>1 && a[l[i]-1]>=a[i]) l[i]=l[l[i]-1];
    for(int i=n-1;i>=1;i--) while(r[i]<n && a[r[i]+1]>=a[i]) r[i]=r[r[i]+1];
            //单调栈处理
    long long ans=-1;
    for(int i=1;i<=n;i++)
    {
        long long k=a[i]*(sum[r[i]]-sum[l[i]-1]);
        if(k>ans)//若k>ans 更新答案,注意:k==ans 时不要更新答案
        {
            ans=k;
            ll=l[i];
            rr=r[i];
        }
    }
    printf("%lld\n%d %d",ans,ll,rr);
    return 0;
}

若您觉得此篇博客写得不错,请别忘了关注我哦 >_<

猜你喜欢

转载自blog.csdn.net/cza0927/article/details/81412109
今日推荐