2019年南昌网络赛 I题 Max answer (单调栈 + 线段树)

                              Max answer

Alice has a magic array. She suggests that the value of a interval is equal to the sum of the values in the interval, multiplied by the smallest value in the interval.

Now she is planning to find the max value of the intervals in her array. Can you help her?

Input

First line contains an integer n(1 \le n \le 5 \times 10 ^5n(1≤n≤5×105).

Second line contains nn integers represent the array a (-10^5 <= a[i] <= 10^5)

Output

One line contains an integer represent the answer of the array.

样例输入

5
1 2 3 4 5

样例输出

36

题意:给n个数,从1到n,要求你求一个子区间,使得这个子区间的和乘以这个子序列的最小值的乘积最大。(n < 5e5   -10^5 <= a[i] <= 10^5)

这道题与poj2796很像,只不是poj上这道题的数据范围只有正数,所以还是有一点差距的。poj2796这道题是一道典型的单调栈的例题.,对于这道题我们也可以使用单调栈来做这道题,只不过是在处理负数的时候要用线段数求区间。

首先我们枚举1到n每个点,以每个点作为区间的最小值。向左边找到第一个比它小的数,向右边找到第一个比它小的数,也就是当前值作为最小值能够影响的最大区间。因为正数乘正数为正数,负数乘以负数为正数,所以,对于每个正数,我们用该数乘以这个区间和。区间和用前缀和来求,对于每个数所能影响的最大区间我们已经求出来了。对于负数来说,我们需要求在它右区间的最小前缀和减去左区间的最大前缀和,如果左区间的最大值小于0,我们用0来表示,因为负数减负数,负数会变小。对于右区间的最小前缀和,以及左区间的最大前缀和我们用线段树来求。。。。。。。。。

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
const int INF = 0x3f3f3f3f;
const int Maxn = 5e5 + 10;
int L[Maxn] , R[Maxn] ;
ll a[Maxn] , sum[Maxn] ;
ll minn[Maxn * 4] , maxn[Maxn*4];
int n;
//建树
void build(int l ,int r,int root){
    if(l == r){
        minn[root] = maxn[root] = sum[l];
        return;
    }
    int mid =(l + r)>>1;
    build(l , mid , root<<1);
    build(mid + 1 , r ,root<<1|1);
    maxn[root] = max(maxn[root<<1] ,maxn[root<<1|1]);
    minn[root] = min(minn[root<<1] ,minn[root<<1|1]);
}
//求最大值
ll qmax(int l , int r , int root ,int ql ,int qr){
    if(l >= ql && r<= qr){
        return maxn[root];
    }
    int mid = l+ r >>1;
    ll ans = -1e8;
    if(mid >= ql)
        ans = qmax(l ,mid , root * 2 ,ql ,qr);
    if(mid < qr)
        ans = max(ans ,qmax(mid + 1 ,r,root * 2 + 1,ql , qr));
    return ans;
}
//求最小值
ll qmin(int l ,int r , int root ,int ql ,int qr){
    if(l >= ql && r <= qr) return minn[root];
    int mid = (l + r )>> 1;
    ll ans = 1e18;
    if(mid >= ql)
        ans = qmin(l ,mid , root * 2,ql,qr);
    if(mid < qr)
        ans = min(ans ,qmin(mid + 1,r ,root * 2 + 1 , ql,qr));
    return ans;
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);

    scanf("%d",&n);
    for(int i = 1;i <= n;i++){
        scanf("%lld",&a[i]);
        sum[i] = sum[i-1] + a[i];
        L[i] = i;R[i] = i;//对于每一个点,左右能够影响区间
    }
    build(1 , n , 1);
    a[0] = a[n + 1] = -INF;//跳出循环
//找每个点的左区间
    for(int i = 1;i <= n;i++){
        while(a[i] <= a[L[i] - 1]){
            L[i] = L[L[i] - 1];  //优化
        }
    }
//反向遍历求出每个点的右区间
    for(int i = n;i >= 1;i--){
        while(a[i] <= a[R[i] + 1]){
            R[i] =R[R[i]+1]; //优化
        }
    }

    ll ans = -1e18;
    for(int i = 1;i <= n;i++){
        if(a[i] > 0){ //对于每一点 如果为正数,直接求
            ans = max(ans , a[i] * (sum[R[i]] - sum[L[i] - 1]));
        }else{//如果为负数的话,用右区间的最小前缀和减去左区间的最大前缀和
            ans = max(ans , a[i]*(qmin(1 , n , 1 , i, R[i]) - max(0ll,qmax(1 , n , 1 , L[i] , i))));
        }
    }
    printf("%lld\n",ans);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值