UVA-1619 Feel Good (单调队列)

题目大意:给一个非负整数序列,求出一个使得区间和乘以区间最小值最大的区间。

题目分析:单调队列。维护两个数组,l[i]表示以a[i]为最小值的左半区间的最左边端点,r[i]表示以a[i]为最小值的右半区间的最右边端点,l[i]和r[i]合起来便是以a[i]为最小值的整个区间。枚举一遍 i 即可。

注意:UVA上的这道题有个大大的坑,明明说可以输出任意一个区间(多个解时),实际上是骗人的!!!

 

代码如下:

# include<iostream>
# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
# define LL long long

struct Num
{
    int val,id;
};
Num a[100005],que[100005];
int n,Left[100005],Right[100005];
long long sum[100005];

void f()
{
    int head=0,tail=-1;
    for(int i=1;i<=n;++i){
        Left[i]=i;
        while(head<=tail&&que[tail].val>=a[i].val)
            --tail;
        if(head<=tail)
            Left[i]=que[tail].id+1;
        else
            Left[i]=1;
        que[++tail]=a[i];
    }
    head=0,tail=-1;
    for(int i=n;i>=1;--i){
        Right[i]=i;
        while(head<=tail&&que[tail].val>=a[i].val)
            --tail;
        if(head<=tail)
            Right[i]=que[tail].id-1;
        else
            Right[i]=n;
        que[++tail]=a[i];
    }
}

int main()
{
    int flag=0,p;
    while(~scanf("%d",&n))
    {
        if(flag)
            printf("\n");
        flag=1;
        sum[0]=0;
        for(int i=1;i<=n;++i){
            scanf("%d",&a[i].val);
            a[i].id=i;
            sum[i]=(LL)a[i].val+sum[i-1];
        }
        f();
        int ansl,ansr;
        long long ans=-1;
        for(int i=1;i<=n;++i)
            if(ans<(LL)a[i].val*(sum[Right[i]]-sum[Left[i]-1]))
                ans=(LL)a[i].val*(sum[Right[i]]-sum[Left[i]-1]),ansl=Left[i],ansr=Right[i];
        printf("%lld\n",ans);
        if(ans==0)
            printf("1 1\n");
        else
            printf("%d %d\n",ansl,ansr);
    }
    return 0;
}

  

posted @ 2015-10-14 21:19  20143605  阅读(256)  评论(0编辑  收藏  举报