蓝桥杯 2026 省 A 综合应变指标 题解

原题链接

Description

给定长度为 \(N\) 的整数序列 \(A_1, A_2, \dots, A_N\)。将该序列拆分为 \(4\) 个连续非空子段:

  1. \(A_1,\dots,A_i\)
  2. \(A_{i+1}\dots,A_j\)
  3. \(A_{j+1},\dots,A_k\)
  4. \(A_{k+1},\dots,A_N\)

其中,\(1\le i< j<k < N\)。

试求 \(\displaystyle \left\lvert{A_1+\dots+A_i}\right\rvert+\left\lvert{A_{i+1}+\dots+A_j}\right\rvert+\left\lvert{A_{j+1}+\dots+A_k}\right\rvert+\left\lvert{A_{k+1}+\dots+A_N}\right\rvert\) 的最大值。

Solution

看到连续子段和,立即想到前缀和。前缀和用 \(S\) 表示,则目标式可化为

\[\left\lvert{S_i}\right\rvert+\left\lvert{S_j-S_i}\right\rvert+\left\lvert{S_k-S_j}\right\rvert+\left\lvert{S_N-S_k}\right\rvert \]

观察上式,如果固定 \(j\),那么前两项只与 \(i\) 有关,后两项只与 \(k\) 有关,且 \(i\) 和 \(k\) 的选择相互独立。

因此,对于每个 \(j\),我们可以分别求出:

\[L_j = \max_{1\le i < j} \big( |S_i| + |S_j - S_i| \big) \]

\[R_j = \max_{j < k < N} \big( |S_k - S_j| + |S_N - S_k| \big) \]

那么答案就是 \(\max\limits_{2\le j\le N-2} \big( L_j + R_j \big)\)。

接下来讨论如何快速计算 \(L_j\) 和 \(R_j\)。只分析 \(L_j\),\(R_j\) 类似处理即可。

对于 \(L_j = \max_{1\le i<j} \big( |S_i| + |S_j - S_i| \big)\),令 \(x = S_i\),\(c = S_j\),则我们需要在 \(x\) 的取值集合 \(\{S_1, S_2, \dots, S_{j-1}\}\) 中最大化:

\[h(x) = |x| + |c - x| \]

函数 \(h(x)\) 的几何意义是数轴上点 \(x\) 到 \(0\) 和到 \(c\) 的距离之和。这是一个下凸函数,下凸函数在闭区间上的最大值一定出现在区间端点。于是,\(h(x)\) 的最大值必然在 \(x\) 取最小值或最大值时取到。

因此,我们只需考虑左边部分的前缀和最大值 \(\text{pre_mx}_{j-1}\) 和最小值 \(\text{pre_mn}_{j-1}\),依下式即得 \(L_j\)。

\[L_j = \max\big( |\text{pre_mx}_{j-1}| + |S_j - \text{pre_mx}_{j-1}|,\; |\text{pre_mn}_{j-1}| + |S_j - \text{pre_mn}_{j-1}| \big) \]

本算法时间复杂度 \(O(N)\)。

Code

#include <bits/stdc++.h>
typedef long long i64;
using namespace std;

const int MAXN = 1e5;
i64 ans, sum[MAXN + 1];
i64 pre_mx[MAXN + 1], pre_mn[MAXN + 1], suf_mx[MAXN + 1], suf_mn[MAXN + 1];

int main()
{
    int n;
    scanf("%d", &n);

    for (int i = 1; i <= n; ++i)
    {
        scanf("%lld", sum + i);
        sum[i] += sum[i - 1];
    }

    pre_mx[1] = pre_mn[1] = sum[1];

    for (int i = 2; i <= n; ++i)
    {
        pre_mx[i] = max(pre_mx[i - 1], sum[i]);
        pre_mn[i] = min(pre_mn[i - 1], sum[i]);
    }

    suf_mx[n] = suf_mn[n] = sum[n] - sum[n - 1];

    for (int i = n - 1; i >= 1; --i)
    {
        suf_mx[i] = max(suf_mx[i + 1], sum[n] - sum[i - 1]);
        suf_mn[i] = min(suf_mn[i + 1], sum[n] - sum[i - 1]);
    }

    for (int j = 2; j <= n - 2; ++j)
    {
        i64 cand1_L = abs(pre_mx[j - 1]) + abs(sum[j] - pre_mx[j - 1]);
        i64 cand2_L = abs(pre_mn[j - 1]) + abs(sum[j] - pre_mn[j - 1]);
        i64 L = max(cand1_L, cand2_L);

        i64 cand1_R = abs(suf_mx[j + 2]) + abs(sum[n] - sum[j] - suf_mx[j + 2]);
        i64 cand2_R = abs(suf_mn[j + 2]) + abs(sum[n] - sum[j] - suf_mn[j + 2]);
        i64 R = max(cand1_R, cand2_R);

        ans = max(ans, L + R);
    }

    printf("%lld\n", ans);
    return 0;
}

Addition

如果拆分出的子段数量更多,上述方法便不再适用。我们可以使用更高级的算法解决此类区间分拆问题,详见OI Wiki。

posted @ 2026-04-15 18:55  SHUddol  阅读(12)  评论(0)    收藏  举报