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 exact scores for a moment. Pick a threshold and only ask: can the minimizer force the final written number to be at most ?
After thresholding, every original leaf is either good () or bad (). The actual labels disappear; the game only cares which color survives.
Compute a value bottom-up: good leaf , bad leaf , internal node of the sum over its children.
The sign is the whole game: means wins no matter whose turn it is, means wins no matter whose turn it is, and means the player to move can force their own color.
So for threshold , the minimizer wins exactly when . This predicate is monotone in , so binary search the first winning threshold.
Research check: the intended route is the binary-search plus signed tree game from the official tutorial PDF, cross-checked against the Codeforces statement and status page showing accepted C++ submissions for B. Sources: statement, official tutorial PDF, status. No submission code is copied.
Threshold Game
Instead of trying to directly minimize/maximize the final label, fix a candidate value .
Color each original leaf:
Now the minimizer wants an leaf to remain, while the maximizer wants an leaf to remain. If we can decide whether the first player wins this threshold game, then the real answer is the smallest winning .
Important: only original leaves have written numbers. If an internal node becomes a leaf later, it does not suddenly get a label. Sneaking that assumption in is a great way to produce very confident garbage.
The Bottom-Up Value
For a fixed , define :
So every subtree is summarized by one of three states.
Meaning Of The Sign
For the threshold game inside a subtree rooted at :
Here is the proof idea.
A single move removes one subtree, so it only changes values along one ancestor path. At every ancestor, the child contribution changes by at most in the ordered set , so the ancestor's sign also cannot jump from straight to or from straight to .
If , there is a path through zero-valued nodes until we reach a zero-valued node with both a positive child and a negative child. Why must this exist? A labeled leaf is never zero, so if a zero node with written leaves does not already have mixed signs, we keep descending into a zero child that still contains written leaves. Then:
Thus a zero state is a first-player win for the threshold game.
If , the player can keep the position nonnegative when moving: remove a non-positive child if one exists; otherwise all meaningful children are positive, and removing one still does not give a decisive negative state. If moves from , the one-move-change lemma says the result is still or , and then wins from there. The case is perfectly symmetric.
For the real game, the minimizer moves first, so threshold is winnable exactly when:
Binary Search
As increases, some leaves switch from to , never the other way around. Since every internal value is just the sign of a sum of monotone child values, is monotone too. Therefore the predicate is monotone, and we binary search the smallest valid .
Because parents satisfy , every child has a larger index than its parent. That means one reverse loop from down to computes all values. No DFS stack, no recursion-depth drama.
Edge cases:
Each check is , and binary search does checks. Total complexity is time and memory.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int n;
cin >> n;
vector<vector<int>> children(n + 1);
for (int i = 2; i <= n; ++i) {
int p;
cin >> p;
children[p].push_back(i);
}
vector<int> dp(n + 1);
auto firstWins = [&](int x) {
for (int u = n; u >= 1; --u) {
if (children[u].empty()) {
dp[u] = (u <= x ? 1 : -1);
} else {
int sum = 0;
for (int v : children[u]) sum += dp[v];
dp[u] = (sum > 0) - (sum < 0);
}
}
return dp[1] >= 0;
};
int lo = 1, hi = n;
while (lo < hi) {
int mid = (lo + hi) / 2;
if (firstWins(mid)) hi = mid;
else lo = mid + 1;
}
cout << lo << '\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();
int n;
cin >> n;
vector<vector<int>> children(n + 1);
for (int i = 2; i <= n; ++i) {
int p;
cin >> p;
children[p].push_back(i);
}
vector<int> dp(n + 1);
auto firstWins = [&](int x) {
for (int u = n; u >= 1; --u) {
if (children[u].empty()) {
dp[u] = (u <= x ? 1 : -1);
} else {
int sum = 0;
for (int v : children[u]) sum += dp[v];
dp[u] = (sum > 0) - (sum < 0);
}
}
return dp[1] >= 0;
};
int lo = 1, hi = n;
while (lo < hi) {
int mid = (lo + hi) / 2;
if (firstWins(mid)) hi = mid;
else lo = mid + 1;
}
cout << lo << '\n';
}