1. 程式人生 > >"###Feel Good"

"###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;
}

若您覺得此篇部落格寫得不錯,請別忘了關注我哦 >_<