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.
Start with a path. Snorlax’s most annoying move is often not moving: if he keeps cooldown at , he can wait until Cyndaquil commits to a side, then block the next critical edge. Lazy Snorlax is optimal Snorlax. Gross, but true.
Think of leaves as already winning positions for Cyndaquil. Now ask: if there are two already-winning vertices nearby, can one blocked edge stop Cyndaquil from reaching both?
The key closure rule is: if two winning vertices satisfy , then every vertex on the path between them is also winning.
Root the tree at the queried start vertex . For each subtree, you do not need the whole set of winning vertices; keep only the distance to the closest winning vertex in that subtree, unless the current vertex is winning itself.
For a node , let the two smallest child values be . The nearest winning vertices in those two child subtrees are at distances and from , so their mutual distance is . Thus becomes winning iff , i.e. . Otherwise return .
Call a vertex good if Cyndaquil can force reaching a leaf from there. Obviously, every leaf is good.
The main observation is a closure rule:
Why? Stand at some vertex on that path. Snorlax can block at most one edge, so he cannot block both directions toward and toward . Cyndaquil moves toward an unblocked side. If Snorlax has just reset the cooldown, then from any internal point the chosen good endpoint is at distance at most , so Cyndaquil reaches it before Snorlax can change the blocked edge again. If Snorlax waits and reacts one move later, Cyndaquil either already reached the chosen endpoint, or can switch to the other endpoint within moves. Then we use the strategy from that good endpoint. One blocked edge is not a wall; it is duct tape.
Let be the smallest set containing all leaves and closed under the rule above. If Cyndaquil starts outside , Snorlax can keep him outside forever.
Look at the connected component of containing Cyndaquil. It has no leaves, since all leaves are in . Snorlax waits with cooldown until Cyndaquil is about to cross an edge from this component into , then blocks exactly that edge.
Suppose two boundary exits are and , where are outside and . If
then
so the closure rule would have put the whole path, including and , into . Contradiction. Therefore different exits are at least moves apart inside the bad component. After Snorlax blocks one exit and resets cooldown to , Cyndaquil needs at least moves just to reach another boundary vertex, and one more move to cross; by then Snorlax is ready again. So outside is a real jail, not a skill issue.
Therefore the answer is YES exactly when .
Root the tree at . For each node , compute:
A rooted leaf has .
For an internal node , collect the values of its children. Let the two smallest be .
If there are two child subtrees, the closest good vertices from those two sides have distance
from each other through . So becomes good iff
If this happens, set . Otherwise, the closest good vertex below is through the best child, so
If has only one child, no pair can be formed through , so the same formula applies.
Finally, print YES iff
The DP is linear:
per test case, and
over the whole input. I also checked the recurrence against the official Codeforces tutorial: https://codeforces.com/blog/entry/151886.
#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 T;
cin >> T;
const int INF = 1e9;
while (T--) {
int n, k, v;
cin >> n >> k >> v;
vector<vector<int>> adj(n + 1);
for (int i = 0; i < n - 1; i++) {
int a, b;
cin >> a >> b;
adj[a].push_back(b);
adj[b].push_back(a);
}
vector<int> parent(n + 1, -1), order;
order.reserve(n);
parent[v] = 0;
order.push_back(v);
for (int i = 0; i < (int)order.size(); i++) {
int x = order[i];
for (int y : adj[x]) {
if (y == parent[x]) continue;
parent[y] = x;
order.push_back(y);
}
}
vector<int> dp(n + 1, INF);
for (int i = n - 1; i >= 0; i--) {
int x = order[i];
int best1 = INF, best2 = INF;
for (int y : adj[x]) {
if (parent[y] != x) continue;
int val = dp[y];
if (val < best1) {
best2 = best1;
best1 = val;
} else if (val < best2) {
best2 = val;
}
}
if (best1 == INF) {
dp[x] = 0;
} else if (best2 != INF && best1 + best2 < k) {
dp[x] = 0;
} else {
dp[x] = best1 + 1;
}
}
cout << (dp[v] == 0 ? "YES" : "NO") << '\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 T;
cin >> T;
const int INF = 1e9;
while (T--) {
int n, k, v;
cin >> n >> k >> v;
vector<vector<int>> adj(n + 1);
for (int i = 0; i < n - 1; i++) {
int a, b;
cin >> a >> b;
adj[a].push_back(b);
adj[b].push_back(a);
}
vector<int> parent(n + 1, -1), order;
order.reserve(n);
parent[v] = 0;
order.push_back(v);
for (int i = 0; i < (int)order.size(); i++) {
int x = order[i];
for (int y : adj[x]) {
if (y == parent[x]) continue;
parent[y] = x;
order.push_back(y);
}
}
vector<int> dp(n + 1, INF);
for (int i = n - 1; i >= 0; i--) {
int x = order[i];
int best1 = INF, best2 = INF;
for (int y : adj[x]) {
if (parent[y] != x) continue;
int val = dp[y];
if (val < best1) {
best2 = best1;
best1 = val;
} else if (val < best2) {
best2 = val;
}
}
if (best1 == INF) {
dp[x] = 0;
} else if (best2 != INF && best1 + best2 < k) {
dp[x] = 0;
} else {
dp[x] = best1 + 1;
}
}
cout << (dp[v] == 0 ? "YES" : "NO") << '\n';
}
}