Loading

P9604 [IOI 2023] 超车 解题报告

简单题。

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

image

由数据范围,这题支持 \(\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。

image

posted @ 2026-08-14 20:36  Trent900  阅读(14)  评论(0)    收藏  举报