把每段移动转化为初始能量下界,用前缀最大值和后缀最大值合并每个失效补给的影响。
OJ: shumeng
题目 ID: CSP202412B
难度:普及-
标签:前缀和前缀后缀最值模拟
日期: 2026-07-31 16:21
形式化题目
依次经过区域
对每个区域
思路
暴力做法是对每个失效位置重新模拟一遍全程,
朴素做法:逐位置模拟
先看直接做法,对每个失效补给独立模拟一次能量变化。
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-31 16:21
* update_at: 2026-08-17 22:39
*/
// brute.cpp:小数据暴力解,对每个失效位置逐段模拟能量变化,复杂度 O(n^2)。
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<long long> a(n + 1), b(n + 1);
for (int i = 0; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) cin >> b[i];
// 对每个可能的失效补给位置独立模拟一遍全程
for (int failure = 1; failure <= n; failure++) {
long long gain = 0;
long long answer = 0;
// gain 表示当前已经发生的净能量变化,answer 是目前必须携带的初始能量
for (int j = 0; j <= n; j++) {
answer = max(answer, a[j] - gain);
gain -= a[j];
// 第 failure 个补给失效,跳过它,其余补给照常获得
if (j < n && j + 1 != failure) gain += b[j + 1];
}
if (failure > 1) cout << ' ';
cout << answer;
}
cout << '\n';
return 0;
}每个失效位置都扫描全程,只适合小数据验证。
建立初始能量下界
定义
其中不存在的补给和记作
失效补给的影响
当第
- 对
,第 段移动发生在失效之前,约束仍是 ; - 对
,少获得了 ,约束变成 。
因此
预处理 base 的前缀最大值与后缀最大值后,每个
样例推演
下表展示第一个样例各数组的关系:
| 0 | 1 | 2 | 3 | |
|---|---|---|---|---|
| 5 | 5 | 5 | 5 | |
| 5 | 10 | -85 | -80 | |
| 前缀最大值 | 5 | 10 | 10 | 10 |
| 后缀最大值 | 10 | 10 | -80 | -80 |
例如
代码
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-31 16:21
* update_at: 2026-08-17 22:39
*/
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
int n;
long long a[MAXN], b[MAXN];
long long base[MAXN];
long long prefix_maximum[MAXN];
long long suffix_maximum[MAXN];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 0; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) cin >> b[i];
// base[j] 是正常补给下,为了通过第 j 段移动所需的初始能量下界。
long long total_a = 0;
long long total_b = 0;
for (int j = 0; j <= n; j++) {
total_a += a[j];
if (j >= 1) total_b += b[j];
base[j] = total_a - total_b;
}
prefix_maximum[0] = base[0];
for (int j = 1; j <= n; j++) {
prefix_maximum[j] = max(prefix_maximum[j - 1], base[j]);
}
suffix_maximum[n] = base[n];
for (int j = n - 1; j >= 0; j--) {
suffix_maximum[j] = max(suffix_maximum[j + 1], base[j]);
}
for (int i = 1; i <= n; i++) {
// 失效补给只会影响第 i 段及之后的约束,之前的约束保持不变。
long long before_failure = prefix_maximum[i - 1];
long long after_failure = b[i] + suffix_maximum[i];
if (i > 1) cout << ' ';
cout << max(before_failure, after_failure);
}
cout << '\n';
return 0;
}复杂度
- 时间:
base、前缀最大值、后缀最大值各扫描一次,。 - 空间:三个
数组。
总结
本题核心是把“能量是否足够”的过程改写成一组关于初始能量的下界。一个补给失效后,只会让它之后的所有约束统一增加同一个值
图示解析
这张图串起从移动过程到单次查询答案的主线:
text
每段消耗与补给
|- 累加得到 base[j]:通过第 j 段的初始能量下界
|- 失效位置之前:保留 base[j]
`- 失效位置之后:统一增加 b[i]
`- 前缀最大值与后缀最大值合并为 w[i]图中的分叉点是失效补给的位置。它不影响之前已经发生的移动,只会让后面的每个初始能量约束增加同一个
