P9604 [IOI 2023] 超车 解题报告
简单题。
额,由于这题单位很特殊,不妨直接对着题目给的图思考。描述一下这个过程,就是上面的线会被迫被下面的线顶着,直到下一个换乘点。

由数据范围,这题支持 \(\mathcal{O}(nm)\) 算法,但是值域稍微有点大。
然后我们发现先处理换乘站 \(i\) 的第 \(j\) 名 \(t(i,j)\) 是比较有前途的做法,而且还可以对我们答案的求解有一定的启发。
这个处理是比较简单的。由上面的描述,每个线只会受到她下面线的影响。因此,我们在 \(i\) 时从下往上遍历模拟即可。
然后,我们考虑询问。对于一辆时间 \(i\) 出发的备用车,我们找到它到达每个超车站的时间即可,这个可以通过二分实现,复杂度 \(\mathcal{O}(m \log n)\)。
进一步地优化:我们尝试直接二分到这辆备用车每一次超车的位置。我们观察到:在第一次在超车点 \(i\) 超车之后,它一定是在这个超车点上的某一个固定的点(时间)继续出发的。而这固定的点只有 \(\mathcal{O}(nm)\) 个。这是不是可以直接在 \(t\) 数组上预处理呢?这样,我们只需要二分出备用车第一次超车是在哪个站的什么时间,然后就可以直接返回这个点预处理的结果了。
这份代码写了二分套二分,没有用任何复杂数据结构,查询复杂度两只 \(\log\)。
#include <bits/stdc++.h>
#define int long long
#define Misaka namespace
#define Network std
using Misaka Network;
const int N = 1007;
pair<int, int> t[N][N];
int nxt[N][N], sum[N][N], pos[N][N], m, x, n;
vector<signed> s;
void init(signed L, signed N, vector<int> T, vector<signed> W, signed X, signed M, vector<signed> S){
m = M, x = X, n = N;
S.insert(S.begin(), 0); s = S;
for(int i = 0; i < N; i ++){
t[1][i] = {T[i], W[i]};
} sort(t[1], t[1] + N);
for(int i = 0; i < N; i ++){
if(i) sum[1][i] = sum[1][i - 1];
if(get<1>(t[1][i]) > X) sum[1][i] ++, pos[1][sum[1][i]] = i;
}
for(int i = 2; i <= M; i ++){
int d = S[i] - S[i - 1], pre = 0;
for(int j = 0; j < N; j ++){
auto [tm, spd] = t[i - 1][j];
tm = max(pre, tm + spd * d);
t[i][j] = {tm, spd}, pre = max(pre, tm);
}
sort(t[i], t[i] + N);
for(int j = 0; j < N; j ++){
if(j) sum[i][j] += sum[i][j - 1];
if(get<1>(t[i][j]) > X) sum[i][j] ++, pos[i][sum[i][j]] = j;
}
}
for(int i = 0; i < N; i ++) nxt[M][i] = get<0>(t[M][i]);
for(int i = M - 1; i >= 1; i --){
for(int j = 0; j < N; j ++){
int cur = get<0>(t[i][j]);
int l = i, r = M + 1, res = -1;
int idx = lower_bound(t[i], t[i] + N, make_pair(cur, 0)) - t[i] - 1;
while(l + 1 < r){
int mid = (l + r) >> 1; int nd = cur + 1ll * (S[mid] - S[i]) * X;
int id = lower_bound(t[mid], t[mid] + N, make_pair(nd, 0)) - t[mid] - 1;
if((idx == -1 ? 0 : sum[i][idx]) == (id == -1 ? 0 : sum[mid][id])) l = mid;
else r = mid, res = pos[mid][(idx == -1 ? 0 : sum[i][idx])];
}
if(res == -1) nxt[i][j] = cur + 1LL * (L - S[i]) * X;
else nxt[i][j] = nxt[r][res];
}
}
}
int arrival_time(int Y){
int l = 0, r = m + 1, res = -1;
int idx = lower_bound(t[1], t[1] + n, make_pair(Y, 0)) - t[1] - 1;
while(l + 1 < r){
int mid = (l + r) >> 1; int nd = Y + s[mid] * x;
int id = lower_bound(t[mid], t[mid] + n, make_pair(nd, 0)) - t[mid] - 1;
if((id == -1 ? 0 : sum[mid][id]) == (idx == -1 ? 0 : sum[1][idx])) l = mid;
else r = mid, res = nxt[r][pos[r][(idx == -1 ? 0 : sum[1][idx])]];
}
if(res == -1) return Y + s[m] * x;
return res;
}
花絮:
早上写完,一直调到了下午,还找同学问了 GPT,一度认为做法假了。晚上回来给 deepseek 充了 50,她拍了一万年发现没开 long long。


浙公网安备 33010602011771号