P10198 [USACO24FEB] Infinite Adventure P

\(\Delta\) 步到的点,考虑倍增。定义 \(jump_{k,i,j}\) 表示当前在 \(i\) 号点,\(t\bmod T_i=j\),跳 \(2^k\) 步到达点。

如果这个 \(t>T_i\),那么跳到一个 \(T_p>T_i\) 时,就不能直接用 \(t\bmod T_i\) 的信息了。但是注意到这样的情况只会出现 \(\log n\) 次,所以我们额外记录 \(lim_{i,j}\) 表示从 \((i,j)\) 开始,一直走直到到了一个 \(T_p>T_i\) 的点,这之前的步数和时间。

那么每次我们求 \((i,j)\)\(step\) 步时,先检查 \(T\) 是否会增加,如果会那么直接用 \(lim\) 的信息,否则用 \(jump\) 跳。那么每次要么 \(T_i\) 翻倍,要么 \(step\) 减半,查询的复杂度为 \(O(\log n\log V)\)

但是我们预处理倍增数组时也要进行这样的查询,总复杂度是 \(O(n\log n\log^2V)\),无法通过。

注意上面的查询过程中,如果路径中每个点层数是类似 1 2 3 .. 20 1 2 3 .. 20 ... 的形式,那么我们每次用 \(jump\) 跳之后就要花 \(\log n\) 的代价重新往上走,这个非常不优。我们希望跳到最高点之后,能连续跳一段最高点。

考虑重新设计一下 \(jump\)\(jump_{k,i,j}\) 表示当前在 \((i,j)\),在不经过 \(T>T_i\) 的前提下,第 \(2^k\) 个经过的满足 \(T_u=T_i\) 的点 \(u\) 以及时间。这个 \(jump\) 可以 \(O(n\log n)\) 预处理。

查询也是好做的,每次如果当前 \((i,j)\) 的层数 \(T_i\) 在之后还会出现,那么就可以通过 \(jump\) 跳到最后一次出现的位置。否则我们暴力走一步。因为不同的 \(T\) 只有 \(\log n\) 种,所以查询复杂度就是 \(\log n\log V\)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const ll inf = 2e18;
const int kN = 1e5 + 5, kLog = 63;
int n, q;
array<int, kN> t, pre;
array<vector<int>, kN> c, vec;
struct Node {
  ll ti;
  int pos;
  bool tag;
  Node() { }
  Node(int _pos, ll _ti, bool _tag) {
    tag = _tag;
    pos = _pos;
    ti = min(_ti, inf);
  }
};
array<array<Node, kN>, kLog> jump;

int Query(int pos, ll ti, ll step) {
  while(step) {
    for(int i = __lg(step); ~i; i--) {
      Node val = jump[i][pre[pos - 1] + (ti & (t[pos] - 1))];
      if(val.tag && (val.ti <= step)) {
        step -= val.ti;
        ti += val.ti;
        pos = val.pos;
      }
    }
    if(!step) return pos;
    pos = c[pos][ti++ & (t[pos] - 1)], step--;
  }
  return pos;
}

int main() {
  // freopen("1.in", "r", stdin);
  // freopen("1.out", "w", stdout);
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> q;
  for(int i = 1; i <= n; i++) {
    cin >> t[i];
    c[i].resize(t[i]);
    vec[t[i]].push_back(i);
    pre[i] = pre[i - 1] + t[i];
  }
  for(int i = 1; i <= n; i++) {
    for(int j = 0; j < t[i]; j++) {
      cin >> c[i][j];
    }
  }
  for(int v = 1; v <= 1e5; v *= 2) {
    for(int x : vec[v]) {
      for(int i = 0; i < v; i++) {
        int cur = x, p = pre[x - 1] + i;
        ll ti = i;
        int nxt = c[cur][ti++ & (v - 1)];
        if(t[nxt] > v) {
          jump[0][p] = Node(x, 0, 0);
          continue;
        }
        cur = nxt;
        while(t[cur] < v) {
          Node info = jump[kLog - 1][pre[cur - 1] + (ti & (t[cur] - 1))];
          if(info.tag) {
            jump[0][p] = Node(0, 0, 0);
            cur = ti = -1;
            break;
          }
          if(info.pos) {
            ti += info.ti;
            cur = info.pos;
          }
          int nxt = c[cur][ti & (t[cur] - 1)];
          if(t[nxt] > v) {
            jump[0][p] = Node(cur, ti - i, 0);
            cur = ti = -1;
            break;
          }else cur = nxt, ti++;
        }
        if(~cur) jump[0][p] = Node(cur, ti - i, 1);
      }
    }
    for(int i = 1; i < kLog; i++) {
      for(int x : vec[v]) {
        for(int j = 0; j < v; j++) {
          int tmp = pre[x - 1] + j;
          Node fir = jump[i - 1][tmp];
          if(!fir.tag) jump[i][tmp] = fir;
          else {
            int p = fir.pos;
            Node sec = jump[i - 1][pre[p - 1] + ((fir.ti + j) & (t[p] - 1))];
            if(!sec.pos) jump[i][tmp] = Node(0, 0, 0);
            else jump[i][tmp] = Node(sec.pos, fir.ti + sec.ti, sec.tag);
          }
        }
      }
    }
  }
  for(int i = 1; i <= q; i++) {
    int pos;
    ll ti, step;
    cin >> pos >> ti >> step;
    cout << Query(pos, ti, step) << "\n";
  }
  return 0;
}
posted @ 2025-04-14 18:29  CJzdc  阅读(45)  评论(0)    收藏  举报