1 条题解
-
0
一、题意整理(关键约束)
给定数组 ,满足条件:
我们需要构造数组 ,满足:
- 是 的子序列。
- (总和不变)。
- 的最大子段和(无聊值)尽可能小。
- 在满足条件3的前提下, 的长度 尽可能小。
求:最终数组 的最小长度 。
二、核心结论(解题关键)
这道题的本质是:用最少的插入次数,消除所有会导致最大子段和超标的区间。
1. 最大子段和下界
题目给出的约束 保证了:
- 数组总和
- 我们能构造出 ,使其最大子段和严格等于 (这是理论最小值,无法更小)。
2. 最小长度公式(标程核心)
最终答案:
- :原数组长度
- :标程中统计的需要插入分隔的段数
- :最后一段是否需要补 或 个分隔(
r != p)
三、算法思路(标程逻辑)
我们遍历数组,维护两个变量:
- :当前区间可达的最小前缀和
- :当前区间可达的最大前缀和
- :数组总和
规则
-
遇到正数
- 扩大区间:
- 若 ,说明必须插入元素分隔,。
-
遇到负数
- 缩小区间:
- 若 ,说明必须插入元素分隔,。
-
最终答案
四、标程代码逐行详解
#include <bits/stdc++.h> namespace FastIO { // 快速读写模板(应对大数据输入) template <typename T> inline T read() { ... } template <typename T> inline void write(T x) { ... } }; using namespace FastIO; #define MAXN 3000001 int a[MAXN]; // 数组开足 3e6 空间
核心函数 solve()
void solve() { int N = read<int>(); // 读入数组长度 n long long p = 0, l = 0, r = 0, v = 0; // 第一步:计算数组总和 p = sum(a) for (int i = 1; i <= N; ++i) p += (a[i] = read<int>());// 第二步:遍历数组,维护 [l, r] 区间 for (int i = 1; i <= N; ++i) { if (a[i] >= 0) { // 处理正数 l += a[i]; r = std::min(r + a[i], p); // 区间非法 → 必须插入分隔,v+1 if (l > r) { ++v; l = a[i]; r = p; } } else { // 处理负数,取绝对值 a[i] = -a[i]; r -= a[i]; l = std::max(l - a[i], 0ll); // 区间非法 → 必须插入分隔,v+1 if (l > r) { ++v; l = 0; r = p - a[i]; } } }// 第三步:计算最终答案 print<int>(v + N + (int)(r != p), '\n'); }
五、公式解释(最关键)
- :原数组必须完整保留。
- :每触发一次区间非法,必须插入 1 个元素分隔。
- :最后一段如果没有闭合,额外补 1 个插入。
六、样例验证
样例 2
输入:
4 2 -3 2 2- 遍历过程触发
- 最后 ,补
- 答案:(与样例完全一致)
七、时间复杂度
- 每组用例:
- 总 之和
- 完美适配 3 秒时限
八、总结(一句话记住)
- 目标:让最大子段和 (总和)。
- 方法:遍历维护前缀和区间 。
- 统计:区间非法时 (必须插入)。
- 答案:。
- 1
信息
- ID
- 6620
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者