POJ[2796]Feel Good 单调栈

题目链接:http://poj.org/problem?id=2796

题目大意:给定一个长度为n的序列,让你在n个数中取一段连续的区间,使这个区间中的最小值乘以这个区间元素和的值最大,并输出左右端点


对于每一个数 ai ,作为某一段区间的最小值,求出保证 ai 最小,它最左可以扩展到 Li ,最右可以扩展到 Ri
暴力的话需要 n2 ,这样显然是超时的
我们可以用单调递增栈来维护,从左到右考虑每一个新加入的数 ai ,如果能使栈中数 x 弹出,则数 x 最右可以扩展到 i1 (因为如果前面还有比它小的数,数 x 就不会存在于栈中),这样我们就可以求出每个数 i 向右可以扩展的最远距离 Ri ,求 Li 只需将该过程反过来即可
统计答案可以用前缀和 q[] 来统计,则每一个数作为最小值的答案就是 a[i](q[R[i]]q[L[i]1])

注意答案有0的情况,可以讲ans初始为-1,也可以讲答案更新条件改成 a[i](q[R[i]]q[L[i]1])ans

还有这道题爆int

代码如下:

#include
#include
#define N 100050
#define int long long
//注意这道题爆int
using namespace std;
int a[N],L[N],R[N],q[N],n,top,ans,l,r;
struct Data{
    int x,k;
    Data(int _=0,int __=0):x(_),k(__){}
}s[N];
main(){
    ans=-1;
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",a+i),q[i]=q[i-1]+a[i];
    for(int i=1;i<=n;i++){
        if(!top) {s[++top]=Data(i,a[i]);continue;}
        if(a[i]>=s[top].k);
        else while(a[i]1;
        s[++top]=Data(i,a[i]);
    }//求R数组
    while(top) R[s[top--].x]=n;//注意这里要将没出栈的元素R值赋为n

    for(int i=n;i>=1;i--){
        if(!top) {s[++top]=Data(i,a[i]);continue;}
        if(a[i]>=s[top].k);
        else while(a[i]1;
        s[++top]=Data(i,a[i]);
    }//求L数组
    while(top) L[s[top--].x]=1;//同上
    for(int i=1;i<=n;i++)
        if(a[i]*(q[R[i]]-q[L[i]-1])>ans){//统计答案,如果ans初值为0,将>ans改为>=ans即可
            ans=a[i]*(q[R[i]]-q[L[i]-1]);
            l=L[i];r=R[i];
        }
    printf("%lld",ans);
    printf("\n%lld %lld\n",l,r);
return 0;
}

你可能感兴趣的:(单调栈,其他题库)