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.
For a fixed root and candidate answer node , only the connected components you get after deleting matter. The rest of the tree's shape is basically decorative noise.
When rooted at , one component around is the “parent side” of if . Nodes from that component are not descendants of , so they can’t be chosen if the LCA is supposed to be .
Let be the number of nodes in 's descendant side, including . If , then is impossible. If , then is possible almost always, because you can choose itself plus descendants. Don’t overcomplicate this part.
So for a fixed ordered pair , contributes to exactly when the component of containing has size at most . For , think of that component size as .
Now flip the counting: sum over instead of roots. For each neighbor-side component size around , exactly roots make that component the parent side. They are valid iff . Add for root . This gives a one-DFS solution after computing subtree sizes.
Let’s count pairs, not roots.
For every root , is the number of vertices that can appear as the LCA of some chosen vertices. So the answer is
That ordered-pair view is the whole problem. The tree tries to look scary by yelling TREE in all caps, but it’s mostly just component sizes.
When can a vertex be the LCA?
Fix a root and a candidate LCA vertex .
In the tree rooted at , for to be the LCA of some nodes, all chosen nodes must be inside the subtree of .
Now here is the key simple point: if the subtree of has at least vertices, then is always achievable.
Why? Pick itself as one of the chosen vertices, then pick any other vertices from its subtree. Since one chosen node is , the LCA is immediately .
So we do not need some annoying “chosen nodes must come from two child subtrees” casework. That would only matter if we were forbidden from choosing itself, and we are not. Classic false assumption trap.
Therefore:
Translate subtree size using components
Delete vertex from the tree. This creates one component for each neighbor of .
If , then the subtree of is the whole tree, so its size is , which is always at least . Thus every vertex contributes once for root .
If , then lies in exactly one component after deleting . Call its size .
When the tree is rooted at , that component is the parent side of , so it is outside the subtree of . Everything else, plus , is inside the subtree.
So:
We need:
Equivalent:
So for a fixed vertex , each surrounding component of size contributes exactly valid roots if , because every root inside that component gives the same parent side.
Also add for .
Thus contribution of vertex is:
The final answer is the sum of that over all .
Computing the component sizes
Root the tree anywhere, say at , just for DFS bookkeeping.
Let be the subtree size of in this arbitrary rooting.
For a vertex :
These are exactly all component sizes around .
So we can do one iterative DFS to compute parents and subtree sizes, then loop over every edge direction/component once.
Initialize:
because every contributes for root .
Then for every component size around every vertex, add if:
That’s it. No reroot DP circus needed.
Complexity
Each edge gives two neighbor-side component sizes total, one from each endpoint’s perspective. So everything is linear:
per test case, and overall. Memory is also .
The answer can be as large as , so use long long.
#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, k;
cin >> n >> k;
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> parent(n + 1, 0), order;
order.reserve(n);
stack<int> st;
st.push(1);
parent[1] = -1;
while (!st.empty()) {
int v = st.top();
st.pop();
order.push_back(v);
for (int to : g[v]) {
if (to == parent[v]) continue;
parent[to] = v;
st.push(to);
}
}
vector<int> sub(n + 1, 1);
for (int i = n - 1; i >= 0; i--) {
int v = order[i];
for (int to : g[v]) {
if (parent[to] == v) sub[v] += sub[to];
}
}
int limit = n - k;
ll ans = n; // r = v is always valid.
for (int v = 1; v <= n; v++) {
for (int to : g[v]) {
int comp;
if (parent[to] == v) comp = sub[to];
else comp = n - sub[v];
if (comp <= limit) ans += comp;
}
}
cout << ans << '\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, k;
cin >> n >> k;
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> parent(n + 1, 0), order;
order.reserve(n);
stack<int> st;
st.push(1);
parent[1] = -1;
while (!st.empty()) {
int v = st.top();
st.pop();
order.push_back(v);
for (int to : g[v]) {
if (to == parent[v]) continue;
parent[to] = v;
st.push(to);
}
}
vector<int> sub(n + 1, 1);
for (int i = n - 1; i >= 0; i--) {
int v = order[i];
for (int to : g[v]) {
if (parent[to] == v) sub[v] += sub[to];
}
}
int limit = n - k;
ll ans = n; // r = v is always valid.
for (int v = 1; v <= n; v++) {
for (int to : g[v]) {
int comp;
if (parent[to] == v) comp = sub[to];
else comp = n - sub[v];
if (comp <= limit) ans += comp;
}
}
cout << ans << '\n';
}
return 0;
}