南昌邀请赛 - I Max answer(单调栈 + 线段树)

题面:

在这里插入图片描述

这题如果a[i] > 0,则是一道经典的单调栈,如果针对a[i]大于0的情况不太清楚的话,可以参考我写的单调栈其他的题目,现在我们重点来讨论a[i] < 0的情况。

如果a[i]小于零,则应该使得包含a[i]的区间和最小,即找到1~i区间上最大的前缀和lmax,和i-n区间上最小的前缀和rmin,然后两者相减得到最小的区间前缀和,很容易联想到用线段树来维护区间前缀的最值,这是比较无脑的算法,但是很好想到:

要注意的是,当左边前缀和的最小值小于0的时候,不用减,因为负数减负数反而会使包含a[i]的最小前缀区间变大

时间复杂度O(nlog(n))

#pragma G++ optimize(2)
#pragma GCC optimize(2)
#include<iostream>
#include<algorithm>
#include<stack>
#include<vector>
#include<iostream>
#include<climits>
#include<queue>
#include<cassert>
#include<iomanip>
#include<cmath>
#include<string> 
#include<cstdio>
#include<cstring>
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define _rev(i, a, b) for(int i = (a); i >= (b); --i)
#define _for(i, a, b) for(int i = (a); i <(b); ++i)
#define _rof(i, a, b) for(int i = (a); i >(b); --i)
#define maxn 500009
#define maxm 109
#define ll long long
#define met(a,b) memset((a),(b), sizeof(a))
#define db double
#define oo INT_MAX
#define eps 1e-8
using namespace std;
ll a[maxn], l[maxn], r[maxn], s[maxn], n, sum[maxn], lmax[maxn], rmax[maxn];
struct node
{
    int l, r, min, max;
}t[maxn * 4];
void update(int o) {
    t[o].min = min(t[o << 1].min, t[o << 1 | 1].min);
    t[o].max = max(t[o << 1].max, t[o << 1 | 1].max);
}
void build(int o, int l, int r) {
    t[o].l = l, t[o].r = r;
    if (l == r) {
         t[o].min = t[o].max = sum[l];
         return;
    }
    ll mid = (l + r) >> 1;
    build(o << 1, l, mid);
    build(o << 1 | 1, mid + 1, r);
    update(o);
}
inline node ask(int o, int l, int r) {
    assert(l <= r);
    if (t[o].l == l && t[o].r == r)return t[o];
    ll mid = (t[o].l + t[o].r) >> 1;
    if (l > mid) return ask(o << 1 | 1, l, r);
    else if (r <= mid)return ask(o << 1, l, r);
    else {
         node a, b, c;
         a = ask(o << 1, l, mid);
         b = ask(o << 1 | 1, mid + 1, r);
         c.min = min(a.min, b.min);
         c.max = max(a.max, b.max);
         return c;
    }
}
int main()
{
    /*ios::sync_with_stdio(false);
    FILE* o =
freopen("C:\\Users\\Jason.Z\\Desktop\\in.txt", "r", stdin);
    FILE* e =
freopen("C:\\Users\\Jason.Z\\Desktop\\out.txt", "w",
stdout);
    assert(o != NULL && e != NULL);
    clock_t q = clock();*/
    while (cin >> n) {
         _rep(i, 1, n) {
             cin >> a[i];
             sum[i] = a[i] + sum[i - 1];
         }
         build(1, 1, n);
         int top = 0;
         _rep(i, 1, n) {
             while (top && a[s[top]] >= a[i]) top--;
             l[i] = top ? s[top] + 1 : 1;
             s[++top] = i;
         }
         top = 0;
         _rev(i, n, 1) {
             while (top && a[s[top]] >= a[i]) top--;
             r[i] = top ? s[top] - 1 : n;
             s[++top] = i;
         }
         ll ans = 0;
         _rep(i, 1, n) {
             if (a[i] > 0) {
                 ans = max(ans, (ll)a[i] * (sum[r[i]] -
sum[l[i] - 1]));
             }
             else {
                 ll lmax = ask(1, 1, i>=2?i - 1: 1).max;
                 ll rmin = ask(1, i, n).min;
                 ll c = lmax <= 0 ? rmin : rmin - lmax;
                 ans = max(ans, c * a[i]);
             }
         }
         cout << ans << endl;
    }
    /*clock_t w =
clock();
    freopen("CON", "w",
stdout);
    cout << 1000.0* (w - q) /
CLOCKS_PER_SEC << "ms" << endl;*/
}

 

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值