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;
}
浙公网安备 33010602011771号