把城市之间的每一段铁路看成一个位置,请求对应区间 `[O,D-1]` 的整体加法,能否接单只取决于这段区间的最大占用是否超过座位数。
OJ: luogu
题目 ID: P8856
难度:普及/提高-
标签:线段树区间最大值区间加建模
日期: 2026-06-21 02:39
题意
火车从 1 号城市开到 C 号城市,一共有 S 个座位。
现在有 R 个订票请求,每个请求是:
- 上车站
O - 下车站
D - 需要
N个座位
按顺序处理这些请求。
如果这批乘客经过的整段路线都还能塞下 N 个座位,就接受这单;否则拒绝。
输出:
T表示接受N表示拒绝
思路
先看一个最容易理解的暴力:
cpp
#include <bits/stdc++.h>
using namespace std;
// brute.cpp:直接维护每一段铁路当前已占用的座位数。
const int MAXC = 60000 + 5;
int used[MAXC];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int city_cnt, seat_cnt, req_cnt;
cin >> city_cnt >> seat_cnt >> req_cnt;
for (int i = 1; i <= req_cnt; i++) {
int o, d, n;
cin >> o >> d >> n;
int mx = 0;
for (int j = o; j <= d - 1; j++) {
mx = max(mx, used[j]);
}
if (mx + n <= seat_cnt) {
cout << "T\n";
for (int j = o; j <= d - 1; j++) {
used[j] += n;
}
} else {
cout << "N\n";
}
}
return 0;
}brute.cpp 直接维护每一段铁路当前被占了多少座位。
对于请求 (O, D, N):
- 先扫描
[O, D-1],看最大占用是多少 - 若还能装下,再把这段全部加上
N
这个思路完全正确,但每次都扫整段,复杂度太高。
真正的建模很直接:
把城市之间的铁路段看成数组位置:
- 第
i个位置表示城市i到i+1这一段铁路
那么一个请求 (O, D, N) 就会影响区间:
[O, D-1]
而能否接受,只取决于这段区间里当前的最大占用 mx:
- 若
mx + N <= S,说明整段铁路都还能装下这批乘客 - 否则至少有一段超载,必须拒绝
于是题目就变成了标准的:
- 区间最大值查询
- 区间加法修改
用懒标记线段树维护即可。
代码
cpp
#include <bits/stdc++.h>
using namespace std;
const int MAXC = 60000 + 5;
int city_cnt, seat_cnt, req_cnt;
int seg_max[MAXC << 2];
int lazy_add[MAXC << 2];
void push_up(int u) {
seg_max[u] = max(seg_max[u << 1], seg_max[u << 1 | 1]);
}
void apply_add(int u, int val) {
seg_max[u] += val;
lazy_add[u] += val;
}
void push_down(int u) {
if (lazy_add[u] == 0) {
return;
}
apply_add(u << 1, lazy_add[u]);
apply_add(u << 1 | 1, lazy_add[u]);
lazy_add[u] = 0;
}
void range_add(int u, int l, int r, int ql, int qr, int val) {
if (ql <= l && r <= qr) {
apply_add(u, val);
return;
}
push_down(u);
int mid = (l + r) >> 1;
if (ql <= mid) {
range_add(u << 1, l, mid, ql, qr, val);
}
if (qr > mid) {
range_add(u << 1 | 1, mid + 1, r, ql, qr, val);
}
push_up(u);
}
int query_max(int u, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) {
return seg_max[u];
}
push_down(u);
int mid = (l + r) >> 1;
int ans = 0;
if (ql <= mid) {
ans = max(ans, query_max(u << 1, l, mid, ql, qr));
}
if (qr > mid) {
ans = max(ans, query_max(u << 1 | 1, mid + 1, r, ql, qr));
}
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> city_cnt >> seat_cnt >> req_cnt;
for (int i = 1; i <= req_cnt; i++) {
int o, d, n;
cin >> o >> d >> n;
// 乘客会占用区间 [o, d-1] 上的每一段铁路。
int current_max = query_max(1, 1, city_cnt - 1, o, d - 1);
if (current_max + n <= seat_cnt) {
cout << "T\n";
range_add(1, 1, city_cnt - 1, o, d - 1, n);
} else {
cout << "N\n";
}
}
return 0;
}复杂度
每个请求需要:
- 一次区间最大值查询
- 最多一次区间加法修改
所以单次复杂度:
总时间复杂度:
空间复杂度:
总结
这题最核心的点是把“城市区间订票”转成“铁路段数组上的区间容量维护”。
一旦完成这个转换,剩下就是标准线段树模板。
一图流解析
这张图把本题的建模、关键转移、实现检查和训练方法压缩到一页,适合读完正文后复盘。
