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.
A path graph on vertices has diameter . So instead of staring at degrees, stare at the diameter. If the current diameter is already , the tree is already a path.
A single sliding operation can increase the distance between any pair of vertices by at most . That means the diameter can increase by at most per operation.
So if the current diameter is , any solution needs at least operations. Now the whole problem becomes: can we always make a move that increases the diameter by exactly ?
Take any diameter path and root the tree at one endpoint. Since the tree is not already a path, some vertex on the diameter has a neighbor outside the diameter.
Let that edge be , where is on the diameter and is outside. If is the parent of on the rooted diameter path, output . This slides the next diameter edge through , making the diameter longer by exactly .
The trick is that this is secretly a diameter problem. The degrees are bait. Pretty convincing bait, but still bait.
A path graph with vertices has diameter .
So if the current tree has diameter , then any final path must somehow grow the diameter from to .
Now look at one sliding operation .
The only local structure that changes is around :
For any two vertices, their old path either does not care about this local change, or it passes through . If it passes through , the distance changes by at most :
So one operation increases the diameter by at most .
Therefore, if the original diameter is , we need at least
operations. No way around it. The diameter is the receipt.
Yes.
Take a diameter path from endpoint to endpoint , and root the tree at .
If the tree is already a path, this diameter contains all vertices, and .
Otherwise, some vertex is not on this diameter path. Since the tree is connected, there is an edge such that:
Also, cannot be the root endpoint . If had a neighbor outside the diameter, then that outside vertex would be even farther from than is, which contradicts the diameter. Same logic rules out the other endpoint too.
Let be the parent of in the rooted tree. So is the previous vertex on the diameter path toward .
Now perform the operation:
What happens?
The next diameter neighbor of toward gets moved from to . So the old diameter path
turns into
That path is exactly one edge longer.
So the diameter increases by at least . From the earlier observation, it cannot increase by more than . Therefore it increases by exactly .
This means we can repeat this idea until the diameter becomes , using exactly operations, which matches the lower bound. Optimal, clean, no DP circus needed.
We only need the first operation.
Algorithm:
parent[u] u v.That operation is guaranteed to be the first move of an optimal sequence.
#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;
while (t--) {
int n;
cin >> n;
vector<vector<int>> g(n + 1);
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
vector<int> dist(n + 1), parent(n + 1);
auto bfs = [&](int start) {
fill(dist.begin(), dist.end(), -1);
fill(parent.begin(), parent.end(), 0);
queue<int> q;
q.push(start);
dist[start] = 0;
while (!q.empty()) {
int v = q.front();
q.pop();
for (int u : g[v]) {
if (dist[u] != -1) continue;
dist[u] = dist[v] + 1;
parent[u] = v;
q.push(u);
}
}
int far = start;
for (int i = 1; i <= n; i++) {
if (dist[i] > dist[far]) far = i;
}
return far;
};
int x = bfs(1);
int y = bfs(x);
if (dist[y] == n - 1) {
cout << -1 << '\n';
continue;
}
vector<int> onDiameter(n + 1, 0);
for (int v = y; v != 0; v = parent[v]) {
onDiameter[v] = 1;
}
int a = -1, b = -1, c = -1;
for (int u = 1; u <= n && a == -1; u++) {
if (!onDiameter[u] || parent[u] == 0) continue;
for (int v : g[u]) {
if (!onDiameter[v]) {
a = parent[u];
b = u;
c = v;
break;
}
}
}
cout << a << ' ' << b << ' ' << c << '\n';
}
return 0;
}#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;
while (t--) {
int n;
cin >> n;
vector<vector<int>> g(n + 1);
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
vector<int> dist(n + 1), parent(n + 1);
auto bfs = [&](int start) {
fill(dist.begin(), dist.end(), -1);
fill(parent.begin(), parent.end(), 0);
queue<int> q;
q.push(start);
dist[start] = 0;
while (!q.empty()) {
int v = q.front();
q.pop();
for (int u : g[v]) {
if (dist[u] != -1) continue;
dist[u] = dist[v] + 1;
parent[u] = v;
q.push(u);
}
}
int far = start;
for (int i = 1; i <= n; i++) {
if (dist[i] > dist[far]) far = i;
}
return far;
};
int x = bfs(1);
int y = bfs(x);
if (dist[y] == n - 1) {
cout << -1 << '\n';
continue;
}
vector<int> onDiameter(n + 1, 0);
for (int v = y; v != 0; v = parent[v]) {
onDiameter[v] = 1;
}
int a = -1, b = -1, c = -1;
for (int u = 1; u <= n && a == -1; u++) {
if (!onDiameter[u] || parent[u] == 0) continue;
for (int v : g[u]) {
if (!onDiameter[v]) {
a = parent[u];
b = u;
c = v;
break;
}
}
}
cout << a << ' ' << b << ' ' << c << '\n';
}
return 0;
}