Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
Forget the whole walk for a second. For a fixed node , ask: for which starting times do we even reach ? The condition to take the -th child of is That’s already a congruence on the original start time.
Try proving by induction on the tree that the set of starting times reaching any node is either empty, or a single residue class Root is easy: all times are . For a child, intersect the parent’s class with one more congruence and use CRT.
Now comes the nice part: if a reachable node has class and , then every such has the same value modulo . So the signpost choice at is already fixed. In other words, that node is fake branching — it’s actually deterministic.
Real branching only happens when . Then going to any reachable child refines the class to modulus Since , the factor is at least , so every real split at least doubles the modulus.
Build a reduced tree of only the real split states. Each state stores the original node and its congruence class . For each child index , solve with CRT. If the new modulus exceeds , then there is at most one query time left in range, so from there just simulate deterministically to the leaf. Query answering is then just walking down this reduced tree, and there are at most about real splits on any path.
The tree is just scenery. The real object is this:
If you are at node at time , you go to child
When starting from the root at time , you reach node at time where is the total edge length from the root to .
So to take the -th child of , the starting time must satisfy that is
That’s the whole game.
For every node , define as the set of starting times for which the walk visits .
Claim: is either empty, or of the form
Why?
So is the intersection of two congruence classes. By CRT, that intersection is either empty or another single congruence class.
Boom. Each reachable node has exactly one pair .
Suppose is reachable with class
If then all such have the same value modulo . Therefore is also fixed, so the chosen child is fixed.
So even if has 17 children, from the point of view of all valid starting times that reach , only one child is possible.
Also, if , it’s obviously deterministic.
That means we can contract chains of deterministic transitions. The only nodes we really care about are the ones where the reachable class still allows several different residues modulo .
Now suppose is reachable with class , and . Then this node is a real split.
For a reachable child, the new modulus becomes
Because , we have so
That’s the killer fact.
Starting from , after at most about real splits, the modulus exceeds . Since all queries satisfy , once the modulus is bigger than , that congruence class contains at most one possible query value.
From that point on, the process is fully deterministic for the query range. No more fancy math, just walk to the leaf.
So the effective branching depth is at most That’s tiny. The big scary tree just collapsed.
We recursively build states of the form:
Then we do this:
While one of the following holds:
we already know the unique child index: So we just move to that child and continue with the same class .
Then this state answers with that leaf.
Here . For each child index , we solve
This creates a reduced tree consisting only of real split states plus terminal leaves.
Each original node is processed only once along its unique root path, so preprocessing stays linear-ish.
For a query :
Eventually you land in a terminal leaf.
Since the reduced depth is at most about , each query is answered in for all practical purposes.
The correctness is basically three tiny facts glued together:
Reachable times for a node form one congruence class.
Proven by induction and CRT.
If , the next child is fixed.
Because all times in that class are identical modulo .
If , every reachable child refines the class to a strictly larger modulus.
So the only genuine branching points are exactly the split states we keep.
The reduced tree preserves all possible query behaviors. Query traversal uses the exact same child rule just skipping nodes where that rule is already predetermined.
So the leaf returned by the reduced tree is exactly the leaf of the original process. No magic, just less useless walking.
Let be the total number of nodes in a test case and the number of queries.
Preprocessing / building the reduced tree: each reachable original node and edge is handled times, plus CRT/gcd work.
Complexity is where , easily fast enough.
Each query walks through at most about split states:
With the given limits, this is comfortably accepted.
Yeah, the problem looks like "tree simulation". It’s not. It’s a CRT automaton wearing a tree costume.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
const ll LIM = 1000000000000000000LL;
const __int128 LIM128 = (__int128)LIM;
struct SplitNode {
int u;
vector<int> to;
};
struct CRTRes {
bool ok;
__int128 x, mod;
};
vector<vector<int>> children_;
vector<ll> dist_;
vector<SplitNode> splits;
ll exgcd(ll a, ll b, ll &x, ll &y) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
ll x1, y1;
ll g = exgcd(b, a % b, x1, y1);
x = y1;
y = x1 - (ll)((__int128)(a / b) * y1);
return g;
}
ll inv_mod(ll a, ll mod) {
ll x, y;
exgcd(a, mod, x, y);
x %= mod;
if (x < 0) x += mod;
return x;
}
CRTRes crt(ll a1, ll m1, ll a2, int m2) {
ll g = std::gcd(m1, (ll)m2);
ll diff = a2 - a1;
if (diff % g != 0) return {false, 0, 0};
ll m2g = m2 / g;
__int128 t = 0;
if (m2g != 1) {
ll aa = (m1 / g) % m2g;
ll inv = inv_mod(aa, m2g);
ll rhs = diff / g;
rhs %= m2g;
if (rhs < 0) rhs += m2g;
t = (__int128)rhs * inv % m2g;
}
__int128 x = (__int128)a1 + (__int128)m1 * t;
__int128 mod = (__int128)m1 * m2g;
x %= mod;
if (x < 0) x += mod;
return {true, x, mod};
}
int walk_single(int u, ll m0) {
while (!children_[u].empty()) {
int d = (int)children_[u].size();
int idx = (int)((m0 + dist_[u]) % d);
u = children_[u][idx];
}
return -u;
}
int build_periodic(int u, ll a, ll M) {
while (true) {
if (children_[u].empty()) return -u;
int d = (int)children_[u].size();
if (d == 1 || M % d == 0) {
int idx = (int)((a + dist_[u]) % d);
u = children_[u][idx];
continue;
}
break;
}
int id = (int)splits.size();
splits.push_back({u, vector<int>((int)children_[u].size(), INT_MIN)});
int d = (int)children_[u].size();
ll du = d;
ll g = std::gcd(M, du);
ll distMod = dist_[u] % du;
for (int idx = 0; idx < d; idx++) {
ll b = idx - distMod;
b %= du;
if (b < 0) b += du;
ll diff = b - a;
if (diff % g != 0) continue;
CRTRes res = crt(a, M, b, d);
if (!res.ok) continue;
int v = children_[u][idx];
if (res.mod <= LIM128) {
splits[id].to[idx] = build_periodic(v, (ll)res.x, (ll)res.mod);
} else if (res.x <= LIM128) {
splits[id].to[idx] = walk_single(v, (ll)res.x);
}
}
return id;
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n, q;
cin >> n >> q;
vector<int> parent(n + 1, 0);
vector<ll> len(n + 1, 0);
children_.assign(n + 1, {});
for (int u = 2; u <= n; u++) {
cin >> parent[u];
children_[parent[u]].push_back(u);
}
for (int u = 2; u <= n; u++) cin >> len[u];
vector<ll> queries(q);
for (int i = 0; i < q; i++) cin >> queries[i];
dist_.assign(n + 1, 0);
for (int u = 2; u <= n; u++) {
dist_[u] = dist_[parent[u]] + len[u];
}
splits.clear();
splits.reserve(n);
int root = build_periodic(1, 0, 1);
for (int i = 0; i < q; i++) {
ll m = queries[i];
int cur = root;
while (cur >= 0) {
const auto &nd = splits[cur];
int d = (int)children_[nd.u].size();
int idx = (int)((m + dist_[nd.u]) % d);
cur = nd.to[idx];
}
if (i) cout << ' ';
cout << -cur;
}
cout << '\n';
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
const ll LIM = 1000000000000000000LL;
const __int128 LIM128 = (__int128)LIM;
struct SplitNode {
int u;
vector<int> to;
};
struct CRTRes {
bool ok;
__int128 x, mod;
};
vector<vector<int>> children_;
vector<ll> dist_;
vector<SplitNode> splits;
ll exgcd(ll a, ll b, ll &x, ll &y) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
ll x1, y1;
ll g = exgcd(b, a % b, x1, y1);
x = y1;
y = x1 - (ll)((__int128)(a / b) * y1);
return g;
}
ll inv_mod(ll a, ll mod) {
ll x, y;
exgcd(a, mod, x, y);
x %= mod;
if (x < 0) x += mod;
return x;
}
CRTRes crt(ll a1, ll m1, ll a2, int m2) {
ll g = std::gcd(m1, (ll)m2);
ll diff = a2 - a1;
if (diff % g != 0) return {false, 0, 0};
ll m2g = m2 / g;
__int128 t = 0;
if (m2g != 1) {
ll aa = (m1 / g) % m2g;
ll inv = inv_mod(aa, m2g);
ll rhs = diff / g;
rhs %= m2g;
if (rhs < 0) rhs += m2g;
t = (__int128)rhs * inv % m2g;
}
__int128 x = (__int128)a1 + (__int128)m1 * t;
__int128 mod = (__int128)m1 * m2g;
x %= mod;
if (x < 0) x += mod;
return {true, x, mod};
}
int walk_single(int u, ll m0) {
while (!children_[u].empty()) {
int d = (int)children_[u].size();
int idx = (int)((m0 + dist_[u]) % d);
u = children_[u][idx];
}
return -u;
}
int build_periodic(int u, ll a, ll M) {
while (true) {
if (children_[u].empty()) return -u;
int d = (int)children_[u].size();
if (d == 1 || M % d == 0) {
int idx = (int)((a + dist_[u]) % d);
u = children_[u][idx];
continue;
}
break;
}
int id = (int)splits.size();
splits.push_back({u, vector<int>((int)children_[u].size(), INT_MIN)});
int d = (int)children_[u].size();
ll du = d;
ll g = std::gcd(M, du);
ll distMod = dist_[u] % du;
for (int idx = 0; idx < d; idx++) {
ll b = idx - distMod;
b %= du;
if (b < 0) b += du;
ll diff = b - a;
if (diff % g != 0) continue;
CRTRes res = crt(a, M, b, d);
if (!res.ok) continue;
int v = children_[u][idx];
if (res.mod <= LIM128) {
splits[id].to[idx] = build_periodic(v, (ll)res.x, (ll)res.mod);
} else if (res.x <= LIM128) {
splits[id].to[idx] = walk_single(v, (ll)res.x);
}
}
return id;
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n, q;
cin >> n >> q;
vector<int> parent(n + 1, 0);
vector<ll> len(n + 1, 0);
children_.assign(n + 1, {});
for (int u = 2; u <= n; u++) {
cin >> parent[u];
children_[parent[u]].push_back(u);
}
for (int u = 2; u <= n; u++) cin >> len[u];
vector<ll> queries(q);
for (int i = 0; i < q; i++) cin >> queries[i];
dist_.assign(n + 1, 0);
for (int u = 2; u <= n; u++) {
dist_[u] = dist_[parent[u]] + len[u];
}
splits.clear();
splits.reserve(n);
int root = build_periodic(1, 0, 1);
for (int i = 0; i < q; i++) {
ll m = queries[i];
int cur = root;
while (cur >= 0) {
const auto &nd = splits[cur];
int d = (int)children_[nd.u].size();
int idx = (int)((m + dist_[nd.u]) % d);
cur = nd.to[idx];
}
if (i) cout << ' ';
cout << -cur;
}
cout << '\n';
}
return 0;
}