#LC1109. 空运物资统计 (差分数组应用)
空运物资统计 (差分数组应用)
我将 LeetCode 1109 题(航班预订统计)改写为了标准的 NOI/CSP 普及组/提高组风格题目。
本题是差分数组(Difference Array)最直接、最标准的应用场景。
[OI 基础训练] 空运物资统计 (差分数组应用)
一、 题目背景与知识点讲解
在之前的题目(如 LeetCode 370 区间加法)中,我们已经接触了差分数组的概念。本题是差分数组在实际业务场景下的直接映射。
场景描述: 你需要对一个长度为 的数组进行 次操作,每次操作都是将连续的一段区间 内的所有数字加上一个特定的值 。最后,你需要输出修改后的数组。
1. 暴力解法 (Brute Force) 对于每一条预订记录,使用循环将对应航班的座位数逐一累加。
- 复杂度:假设航班数 ,预订记录数 。每次操作最坏需遍历 个元素。总时间复杂度 。
- 瓶颈:当 均达到 级别时,运算量为 ,远超计算机每秒 次的运算能力,导致超时 (TLE)。
2. 优化解法:差分数组 (Difference Array) 利用差分数组,我们可以将“区间修改”的时间复杂度从 降低到 。
-
核心思想: 定义差分数组 。 如果我们想让原数组 在区间 上增加 ,在差分数组 上的表现为:
- (这就导致了 及其后面的所有数相对于 都增加了 )
- (为了抵消上面的增加,使得 之后的数不受影响)
-
还原方法: 操作完成后,对差分数组 求前缀和,即可得到最终的原数组 。
-
复杂度分析:
- 修改: 次操作,每次 。
- 还原:遍历一次数组,复杂度 。
- 总复杂度:。
二、 题目描述
IOI 航空公司拥有 架飞机,编号从 到 。 航空公司接到了 份预订订单。每一份订单信息包含三个整数:。 这表示从编号为 的飞机到编号为 的飞机(包含 和 ),每架飞机都需要预留 个座位。
请你计算并输出所有订单处理完毕后,每架飞机总共被预订了多少个座位。
注意: 初始时,所有飞机的预订座位数都为 0。
输入格式
第一行包含两个整数 和 ,分别表示飞机的数量和订单的数量。 接下来 行,每行包含三个整数 ,表示一条预订记录。
输出格式
输出一行,包含 个整数,第 个整数表示第 架飞机的总预订座位数。整数之间用一个空格隔开。
数据范围
- 提示:虽然单个订单座位数不大,但累加后可能会超过
int范围,建议使用long long。
样例输入
5 3
1 2 10
2 3 20
2 5 25
样例输出
10 55 45 25 25
样例解释
- 初始状态:
[0, 0, 0, 0, 0] - 订单 1 (1-2, +10):
[10, 10, 0, 0, 0] - 订单 2 (2-3, +20):
[10, 30, 20, 0, 0] - 订单 3 (2-5, +25):
[10, 55, 45, 25, 25]
三、 标准代码 (C++14 OI风格)
/**
* 题目:空运物资统计 (Corporate Flight Bookings)
* 知识点:差分数组 (Difference Array)
* 时间复杂度:O(N + M)
* 空间复杂度:O(N)
*/
#include <iostream>
#include <vector>
using namespace std;
// 使用 long long 防止累加过程中溢出
// 虽然本题 seats <= 10000,但 M 可达 2*10^5,总和可能达到 2*10^9,接近 int 极限
// 在 OI 中,涉及“求和”通常无脑上 long long 是比较稳妥的策略
typedef long long ll;
const int MAXN = 200005;
// 差分数组,开大一点防止 diff[r+1] 越界
ll diff[MAXN];
int main() {
// 1. IO 优化,加快大量数据的读写速度
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
if (!(cin >> n >> m)) return 0;
// 2. 处理 M 条订单 (构建差分数组)
for (int i = 0; i < m; ++i) {
int l, r, v;
cin >> l >> r >> v;
// 核心差分操作:
// 区间 [l, r] 增加 v
// 相当于 diff[l] += v, diff[r+1] -= v
diff[l] += v;
diff[r + 1] -= v;
}
// 3. 还原原数组 (计算前缀和)并输出
// current_seats 相当于前缀和 sum[i]
ll current_seats = 0;
for (int i = 1; i <= n; ++i) {
current_seats += diff[i];
// 按照题目要求格式输出
cout << current_seats << (i == n ? "" : " ");
}
cout << "\n";
return 0;
}
四、 教学备课重点 (教练参考)
-
模型识别能力:
- 教会学生如何从题目描述中提炼出数学模型。
- 题目表述:“对第 到第 架飞机预订 个座位”。
- 数学模型:“对数组区间 内的所有元素加上 ”。
- 这种**“多次区间修改,一次最终查询”的模式,就是差分数组**的标准信号。
-
边界问题 (Off-by-one Error):
- 题目中 是飞机的编号()。
- 差分操作需要修改
diff[R+1]。 - 易错点:如果数组只开到 ,当 时,
diff[N+1]会导致数组越界。 - 解决方案:数组大小至少开到 。
-
与线段树的取舍:
- 很多学生学了线段树后,看到“区间修改”就想用线段树。
- 引导:线段树支持“边修改、边查询”,复杂度是 。虽然能过,但代码量大且常数大。
- 本题是离线的(所有修改完成后才查询),差分数组 是最优解,且代码极短(核心仅 2 行)。
-
数据类型:
- 强调
long long的重要性。即使题目现在的范围int可能刚好够用,但在 CSP/NOI 中,出题人经常会构造极限数据(例如 很大,且 也很大),导致int溢出变成负数。养成好习惯至关重要。
- 强调