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 letters for a second and watch one vertex. Starting from an internal vertex with a blank slate, Bob visits its left subtree, returns, visits its right subtree, returns, then leaves to the parent.
Define as the number of moves needed to start at and first move to its parent, assuming everything is blank. Then for leaves and for internal vertices.
The actual path from a start vertex is not just its subtree tour. It is the concatenation of the tours of , then , then its parent, and so on up to vertex .
Build an Euler-like array where the local tour of every vertex is a contiguous segment: leaf gives , internal gives .
Let be the total escape time from . For a query , binary-lift to the highest ancestor with ; then the answer is the element at offset inside 's Euler segment.
Research note. I checked the official Codeforces problem page, the official Round 1080 editorial/code at https://codeforces.com/blog/entry/151174, and accepted-submission status evidence from Codeforces. The official prose for G is basically absent, so the proof below is derived from the statement and cross-checked against the official code idea. No source text is copied.
The key is to stop simulating Bob's tiny brain cell one move at a time. The letter states make every subtree behave like a deterministic tour.
For a vertex , define as the number of moves from starting at with all relevant marks blank until Bob first moves from to its parent.
If is a leaf, Bob immediately moves to the parent, so .
If has children and , the behavior is forced:
So
.
Now define as the list of vertices Bob is standing on after offsets during this local tour. Then:
This is just an Euler tour with repeated internal vertices. Crucially, if we build this list for the whole tree rooted at vertex , every is a contiguous segment starting at .
Next, we need total escape time. Let be the time to reach vertex when starting from . Vertex itself is not stored in the input; it is only the parent of vertex .
For vertex :
.
For any other vertex with parent :
.
Why? After Bob finishes the local tour of , he is at , and every mark written inside 's subtree has been erased again. Nothing magical leaks upward. Then the process continues exactly as if it had freshly started at . This gives the concatenation:
.
For a query , suppose the current chunk is some ancestor of . The number of moves spent before entering chunk is:
.
So we need the highest ancestor such that:
.
Then the local offset inside is:
,
and the answer is:
.
We find that ancestor with binary lifting. Since , 20 jump levels are enough.
A couple of edge cases:
Precomputation:
Complexity is time and memory per test case. The Euler array has length , so no giant nonsense hiding there.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
constexpr int LOG = 20;
int tc;
cin >> tc;
while (tc--) {
int n, q;
cin >> n >> q;
vector<int> leftChild(n), rightChild(n), parent(n);
iota(parent.begin(), parent.end(), 0);
for (int i = 0; i < n; i++) {
int l, r;
cin >> l >> r;
--l;
--r;
leftChild[i] = l;
rightChild[i] = r;
if (l != -1) {
parent[l] = i;
parent[r] = i;
}
}
vector<int> order;
order.reserve(n);
vector<int> stack = {0};
while (!stack.empty()) {
int v = stack.back();
stack.pop_back();
order.push_back(v);
if (leftChild[v] != -1) {
stack.push_back(leftChild[v]);
stack.push_back(rightChild[v]);
}
}
vector<ll> subTime(n), escapeTime(n);
for (int i = n - 1; i >= 0; i--) {
int v = order[i];
if (leftChild[v] == -1) {
subTime[v] = 1;
} else {
subTime[v] = subTime[leftChild[v]] + subTime[rightChild[v]] + 3;
}
}
vector<array<int, LOG>> up(n);
vector<int> tin(n), euler;
euler.reserve(2 * n - 1);
for (int j = 0; j < LOG; j++) up[0][j] = 0;
escapeTime[0] = subTime[0];
auto prepareChild = [&](int child, int par) {
escapeTime[child] = escapeTime[par] + subTime[child];
up[child][0] = par;
for (int j = 1; j < LOG; j++) {
up[child][j] = up[up[child][j - 1]][j - 1];
}
};
vector<pair<int, int>> dfs;
dfs.push_back({0, 0});
while (!dfs.empty()) {
auto [v, state] = dfs.back();
dfs.pop_back();
if (state == 0) {
tin[v] = (int)euler.size();
euler.push_back(v);
if (leftChild[v] != -1) {
prepareChild(leftChild[v], v);
dfs.push_back({v, 1});
dfs.push_back({leftChild[v], 0});
}
} else if (state == 1) {
euler.push_back(v);
prepareChild(rightChild[v], v);
dfs.push_back({v, 2});
dfs.push_back({rightChild[v], 0});
} else {
euler.push_back(v);
}
}
for (int qi = 0; qi < q; qi++) {
int v;
ll k;
cin >> v >> k;
--v;
int w = v;
for (int j = LOG - 1; j >= 0; j--) {
int anc = up[w][j];
if (escapeTime[v] - escapeTime[anc] <= k) {
w = anc;
}
}
ll offset = k - (escapeTime[v] - escapeTime[w]);
int ans = euler[tin[w] + offset] + 1;
cout << ans << (qi + 1 == q ? '\n' : ' ');
}
}
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
constexpr int LOG = 20;
int tc;
cin >> tc;
while (tc--) {
int n, q;
cin >> n >> q;
vector<int> leftChild(n), rightChild(n), parent(n);
iota(parent.begin(), parent.end(), 0);
for (int i = 0; i < n; i++) {
int l, r;
cin >> l >> r;
--l;
--r;
leftChild[i] = l;
rightChild[i] = r;
if (l != -1) {
parent[l] = i;
parent[r] = i;
}
}
vector<int> order;
order.reserve(n);
vector<int> stack = {0};
while (!stack.empty()) {
int v = stack.back();
stack.pop_back();
order.push_back(v);
if (leftChild[v] != -1) {
stack.push_back(leftChild[v]);
stack.push_back(rightChild[v]);
}
}
vector<ll> subTime(n), escapeTime(n);
for (int i = n - 1; i >= 0; i--) {
int v = order[i];
if (leftChild[v] == -1) {
subTime[v] = 1;
} else {
subTime[v] = subTime[leftChild[v]] + subTime[rightChild[v]] + 3;
}
}
vector<array<int, LOG>> up(n);
vector<int> tin(n), euler;
euler.reserve(2 * n - 1);
for (int j = 0; j < LOG; j++) up[0][j] = 0;
escapeTime[0] = subTime[0];
auto prepareChild = [&](int child, int par) {
escapeTime[child] = escapeTime[par] + subTime[child];
up[child][0] = par;
for (int j = 1; j < LOG; j++) {
up[child][j] = up[up[child][j - 1]][j - 1];
}
};
vector<pair<int, int>> dfs;
dfs.push_back({0, 0});
while (!dfs.empty()) {
auto [v, state] = dfs.back();
dfs.pop_back();
if (state == 0) {
tin[v] = (int)euler.size();
euler.push_back(v);
if (leftChild[v] != -1) {
prepareChild(leftChild[v], v);
dfs.push_back({v, 1});
dfs.push_back({leftChild[v], 0});
}
} else if (state == 1) {
euler.push_back(v);
prepareChild(rightChild[v], v);
dfs.push_back({v, 2});
dfs.push_back({rightChild[v], 0});
} else {
euler.push_back(v);
}
}
for (int qi = 0; qi < q; qi++) {
int v;
ll k;
cin >> v >> k;
--v;
int w = v;
for (int j = LOG - 1; j >= 0; j--) {
int anc = up[w][j];
if (escapeTime[v] - escapeTime[anc] <= k) {
w = anc;
}
}
ll offset = k - (escapeTime[v] - escapeTime[w]);
int ans = euler[tin[w] + offset] + 1;
cout << ans << (qi + 1 == q ? '\n' : ' ');
}
}
}