南昌网络赛 Max answer(单调栈 + 线段树维护前缀和最大最小值)

题目链接

题意:

给一串序列,要求选任意连续的一段区间,使得这一段区间中最小值乘以区间总和尽量大。

题解:

首先先单调栈求出每个数字作为最小值所在的区间。

如果数据范围只有正数那么只用单调栈就可以解决,但是数据有负数,那么怎么高效查询使得总乘积最大的区间呢?

那么这时候我们可以用线段树维护前缀和的最大最小值,要使得负数乘以一个数尽量大,那么这个数就得尽量小。

前缀和能够 O(1) 的查询出区间的和,要使得区间和尽量小,那么对于第 i 个数字,通过线段树从 i 到 R[i] 中找到前缀和最小的,再从 L[i] 到 i - 1 中找到前缀和最大的(到 i 也行,不影响结果,还能防止 i == 0 时溢出),相减即为包含第 i 个数的最小连续区间和。

代码:

#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <string>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <bitset>
using namespace std;

typedef long long ll;
#define int ll
#define INF 0x3f3f3f3f3f3f3f3f
#define MAXM 100000 + 10
#define MAXN 500000 + 10
const ll mod = 1e9 + 7;
#define P pair<int, int>
#define fir first
#define sec second

int n;
int a[MAXN], sum[MAXN];
int L[MAXN], R[MAXN];
int mintree[MAXN<<2], maxtree[MAXN<<2];

void minbuild(int l, int r, int rt)
{
    if(l == r) {
        mintree[rt] = sum[l];
        return ;
    }

    int mid = (l + r) / 2;
    minbuild(l, mid, rt << 1);
    minbuild(mid + 1, r, rt << 1 | 1);
    mintree[rt] = min(mintree[rt<<1], mintree[rt<<1|1]);
}

void maxbuild(int l, int r, int rt)
{
    if(l == r) {
        maxtree[rt] = sum[l];
        return ;
    }

    int mid = (l + r) / 2;
    maxbuild(l, mid, rt << 1);
    maxbuild(mid + 1, r, rt << 1 | 1);
    maxtree[rt] = max(maxtree[rt<<1], maxtree[rt<<1|1]);
}

int minquery(int l, int r, int rt, int L, int R)
{
    if(L <= l && r <= R) return mintree[rt];

    int ans = INF, mid = (l + r) / 2;
    if(L <= mid) ans = min(ans, minquery(l, mid, rt << 1, L, R));
    if(mid < R) ans = min(ans, minquery(mid + 1, r, rt << 1 | 1, L, R));
    return ans;
}

int maxquery(int l, int r, int rt, int L, int R)
{
    if(L <= l && r <= R) return maxtree[rt];

    int ans = -INF, mid = (l + r) / 2;
    if(L <= mid) ans = max(ans, maxquery(l, mid, rt << 1, L, R));
    if(mid < R) ans = max(ans, maxquery(mid + 1, r, rt << 1 | 1, L, R));
    return ans;
}

signed main()
{
//    ios::sync_with_stdio(false);
//    cin.tie(0); cout.tie(0);
    cin >> n;
    for(int i = 1; i <= n; i ++) cin >> a[i], sum[i] = sum[i-1] +  a[i];

    minbuild(1, n, 1);
    maxbuild(1, n, 1);
    stack<int> S;
    a[0] = a[n+1] = -INF;
    for(int i = 1; i <= n + 1; i ++) {
        while(!S.empty() && a[S.top()] > a[i]) {
            R[S.top()] = i;
            S.pop();
        }
        S.push(i);
    }
    while(!S.empty()) S.pop();
    for(int i = n; i >= 0; i --) {
        while(!S.empty() && a[S.top()] > a[i]) {
            L[S.top()] = i;
            S.pop();
        }
        S.push(i);
    }

    int ans = -INF;
    for(int i = 1; i <= n; i ++) {
        if(a[i] >= 0) {
            ans = max(ans, a[i] * (sum[R[i]-1] - sum[L[i]]));
        }
        else {

            int mq = minquery(1, n, 1, i, R[i] - 1);
            int lq = maxquery(1, n, 1, L[i] + 1, i);
            ans = max(ans, a[i] * (mq - lq));
        }
    }

    cout << ans << endl;
}

/*

老实说,
要到几岁才开始不相信圣诞老人的存在……
这类无聊的话题对我而言,
根本不痛不痒的。
不过,
讲到我从几岁起开始不相信圣诞老人就是那个穿着红衣服的老公公时,
我能确定地说,
我根本打从一开始就不相信。
我知道幼稚园圣诞节庆祝会时出现的圣诞老人是假的,
回溯记忆,
还能记起周围的幼稚园小朋友都一脸不信任地望着假扮圣诞老人的园长老师。
即使没有撞见老妈正在亲吻圣诞老公公,
机灵的我也老早就怀疑只在圣诞节工作的老爷爷是否真的存在了。
不过,
我却是过了很久以后,
才发现外星人、幽灵、妖怪、超能力者以及特摄、动画里头,
那些与邪恶组织战斗的英雄们并不存在这世上。
不,
说不定我早就发现了,
只不过一直不想承认而已。
因为,
在我的内心深处,
是十分渴望那些外星人、幽灵、妖怪、超能力者以及邪恶组织突然出现在眼前的。
和我生活的这个普通世界相比,
特摄、动画里头所描绘的世界,
反而更有魅力。
我也想活在那种世界里!
我真的好想拯救被外星人绑架并关在透明的大型豌豆夹里头的少女…
也想拿着雷射枪运用智慧与勇气击退企图改写历史的未来人…
或者光用一句咒语就收拾了恶灵跟妖怪,
再不然就是和秘密组织的超能力者进行超能力的战斗!
等等,
冷静一下,
假设我被外星人等等(以下略)那类的生物袭击,
没有任何特殊能力的我怎么可能和他们对抗?
于是,
我便如是幻想 ——
某天,
班上突然转来一个谜样的转学生,
他其实是个外星人或未来人那类的生物,
并拥有未知的能力,
后来他跟坏人战斗,
而我只要设法让自己被卷进那场战争就好了。
主要战斗的人是他,
而我则是追随他的小跟班。
啊啊,
实在太棒了,
我真是聪明啊!
要不然就是这样。
某天,
我那不可思议的力量突然觉醒,
就像隔空取物或精神念力之类的。
而且地球上其实还有很多拥有超能力的人类存在,
自然也会有一个组织专门收容这些人。
不久之后,
善良的组织便派人来迎接我。
而我也成为组织的一员共同对抗企图征服世界的邪恶超能力者。
不过,
现实却是意外地残酷。
现实的生活中,
并没有半个转学生转来我班上,
我也没看过UFO…
就算去了地方上常出现幽灵或妖怪的灵异地点也连个鬼影都没有…
花了两小时盯着桌上的铅笔,
它却连一微米都没移动…
上课时死盯着前座同学的头,
却怎么样也无法读出他在想什么。
我就这样边惊叹世界物理法则经常出现的现象,
边不停自嘲,
不知从何时起,
我就开始不看电视上的UFO特别节目或灵异节目了。
因为不可能会有那种东西……
不过后来我也成长到仅对那方面的事情存有一丝留恋的程度。
国中毕业之后,
我便从那孩提时代的梦想毕业,
逐渐习惯这个世界的平凡。
而让我还有一缕期待的一九九九年也没有发生什么事。
进入二十一世纪后,
人类依旧无法迈出月球到其他星球去。
看这情况,
在我还活着的时候,
想从地球当天往返阿尔法人马座(Alpha Cenauri)似乎是不太可能的。
我脑海中时而幻想着这些事,
终于也没啥感慨地成为高中生 —— 直到遇到了凉宫春日。
之后,
我就这么糊里糊涂地进入学区内的县立高中就读。
起初我还很后悔,
因为这座学校位于很高的山上,
就算是春天也要挥汗如雨地爬上直窜山顶的坡道,
想轻松健行的那份悠闲早已消失无踪。
一想到今后三年每天一大早都得这样爬山,
我的心情就阴郁无比。
或许是早上差点睡过头的关系,
走路的速度自然加快许多,
虽然也曾想过以后干脆早十分钟起床,
慢慢走去上学就不会这么累,
不过一想到临起床前的那十分钟睡眠是多么的宝贵,
我随即放弃了这个念头。
所以,
我相信未来的三年还是得持续这个晨间的运动。
一想到这里,
心情就更沉重了。

*/

猜你喜欢

转载自blog.csdn.net/ooB0Boo/article/details/89486117