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.
Stop trying to draw the MST directly. Run Kruskal mentally instead: for a fixed distance limit , ask how many connected components the selected vertices have if you are allowed to connect pairs whose tree-distance is at most .
For any weighted complete graph with integer weights, its MST weight can be recovered from threshold component counts:
where is the number of connected components among selected vertices using only edges of weight at most . For , every chosen vertex is alone.
In a tree metric, Kruskal merges have a center. When two selected vertices get connected, the path between them in the original tree has a middle vertex or middle edge. Counting by this center is the way out of the subset explosion.
For a fixed center vertex , delete . The remaining connected components are branches. If a subset has selected vertices in several branches, only the closest selected vertex of each branch matters for edges whose path goes through .
For each center , process its branches with DP. A branch can be empty, or its nearest selected vertex can be at distance . Keep the current smallest nearest distance among already nonempty branches; adding this branch contributes . Sum this over all centers.
We need the sum of MST weights over all subsets in the complete graph where edge weights are tree distances.
First, murder the tempting false shortcut: the answer for a subset is not always the size of the Steiner tree in the original tree. On a star with three selected leaves, the Steiner tree has weight , but the metric MST has two edges of weight , total . So yeah, that idea gets packed up and sent home.
The Kruskal Decomposition
Take one fixed subset . Consider its MST in the complete distance graph.
For a tree metric, every chosen MST edge corresponds to the path from to in the original tree. That path has a middle. The middle is either:
So the MST weight can be decomposed by these centers.
It is enough to count contributions from every center independently, then add them all.
Vertex Centers
Fix a vertex .
Delete from the tree. The remaining connected components are the branches around . Also treat itself as a special branch containing one vertex at distance .
For a subset , each branch is either empty or has a closest selected vertex to . Let that closest distance be .
If two selected vertices lie in different branches around , their path goes through , and the cheapest connection between those two branches has weight
So for this center , we reduce the subset to a bunch of nonempty branches with values .
The MST of the complete graph on these branches with edge weights is easy. If
then the optimal tree connects every other branch to the smallest one, giving
Proof is blunt: branch must connect to something, and every edge incident to it costs at least . The star through the minimum branch achieves exactly that lower bound.
Edge Centers
Now handle centers that are original tree edges.
For an edge , removing it splits the tree into two sides. If the closest selected vertex on the side is distance from , and the closest selected vertex on the side is distance from , then the cheapest connection whose middle is this edge has weight
This is the same idea as vertex centers, except there are only two sides and the middle edge contributes .
So every MST edge is counted exactly once: either by its middle vertex or by its middle tree edge.
Counting Choices
For a fixed center and one side/branch, suppose it contains vertices at distance from the center endpoint.
The number of ways for the nearest selected vertex in that branch to be exactly distance is
We choose at least one vertex at distance , choose no vertices closer, and choose anything farther. Empty branch has one way.
DP for a Vertex Center
For a vertex center , process its branches one by one.
Maintain:
When adding a new nonempty branch with nearest distance :
This incremental process produces exactly
for every chosen set of nonempty branches, regardless of processing order. Empty branch just leaves the state unchanged.
DP for an Edge Center
For an edge center , there are only two sides. Count all choices where the nearest selected vertex on the side has distance , and on the side has distance . Their contribution is
Multiply the two side counts and sum.
Implementation Details
For every root/center, run BFS/DFS to get distances. Since and , doing work per center is fine.
For vertex centers, BFS from also identifies the first neighbor of on the path to every vertex, which tells us the branch.
For edge centers, root the tree once per directed edge side by a DFS that avoids crossing the center edge, collecting distance counts.
This gives an solution overall if implemented with sane constants.
Correctness Proof
First, every edge in the metric MST corresponds to a path in the original tree. That path has a unique middle: either a vertex or an original tree edge. Therefore, assigning every MST edge to its middle partitions the MST weight into independent center contributions.
For a fixed vertex center , selected vertices from the same branch around have paths whose middle is not , so they do not contribute to center . Selected vertices from different branches have paths through , and the cheapest possible edge between two such branches uses the closest selected vertex in each branch, with cost .
Thus the center- contribution only depends on the nearest selected distance of every nonempty branch. The induced branch graph has edge weights . Its MST is the star through the smallest , with weight , because each non-minimum branch needs an incident edge costing at least , and the star achieves all those bounds.
The branch DP computes exactly this value for every subset: when a new branch with nearest distance is added to an existing collection with minimum , the new branch contributes the cheapest necessary connection , and the new minimum becomes . Empty branches contribute nothing. Since ways[d] counts exactly the selections with nearest distance , every subset is counted once.
For a fixed edge center xyx+1+y$. The formula sums exactly those choices.
Since every MST edge has exactly one middle, and each center contribution is counted exactly once, summing all vertex-center and edge-center contributions over all subsets gives the required total.
Complexity
The algorithm does work for each of centers, so the total time is per test case. Memory is besides the adjacency list.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MOD = 1000000007;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int addmod(int a, int b) {
a += b;
if (a >= MOD) a -= MOD;
return a;
}
int submod(int a, int b) {
a -= b;
if (a < 0) a += MOD;
return a;
}
int mulmod(ll a, ll b) {
return int(a * b % MOD);
}
int main() {
setIO();
int T;
cin >> T;
const int NMAX = 5000;
vector<int> pw2(NMAX + 1);
pw2[0] = 1;
for (int i = 1; i <= NMAX; i++) pw2[i] = addmod(pw2[i - 1], pw2[i - 1]);
while (T--) {
int n;
cin >> n;
vector<vector<int>> g(n);
vector<pair<int, int>> edges;
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
--u; --v;
g[u].push_back(v);
g[v].push_back(u);
edges.push_back({u, v});
}
vector<vector<int>> dist(n, vector<int>(n, -1));
for (int s = 0; s < n; s++) {
queue<int> q;
dist[s][s] = 0;
q.push(s);
while (!q.empty()) {
int v = q.front();
q.pop();
for (int to : g[v]) {
if (dist[s][to] != -1) continue;
dist[s][to] = dist[s][v] + 1;
q.push(to);
}
}
}
auto makeWays = [&](const vector<int>& cnt, int mx) {
vector<int> ways(mx + 1, 0);
int farther = 0;
for (int d = mx; d >= 0; d--) {
if (cnt[d]) ways[d] = mulmod(submod(pw2[cnt[d]], 1), pw2[farther]);
farther += cnt[d];
}
return ways;
};
int ans = 0;
vector<int> dp(n + 1), ndp(n + 1), sum(n + 1), nsum(n + 1);
vector<int> active, nactive;
auto applyBranch = [&](const vector<int>& ways) {
fill(ndp.begin(), ndp.end(), 0);
fill(nsum.begin(), nsum.end(), 0);
nactive.clear();
auto push = [&](int i) {
if (ndp[i] == 0 && nsum[i] == 0) nactive.push_back(i);
};
for (int m : active) {
push(m);
ndp[m] = addmod(ndp[m], dp[m]);
nsum[m] = addmod(nsum[m], sum[m]);
}
for (int x = 0; x < (int)ways.size(); x++) {
int w = ways[x];
if (!w) continue;
push(x);
ndp[x] = addmod(ndp[x], w);
for (int m : active) {
int nm = min(m, x);
int waysAdd = mulmod(dp[m], w);
int sumAdd = addmod(mulmod(sum[m], w), mulmod(waysAdd, m + x));
push(nm);
ndp[nm] = addmod(ndp[nm], waysAdd);
nsum[nm] = addmod(nsum[nm], sumAdd);
}
}
for (int m : active) dp[m] = sum[m] = 0;
active.clear();
for (int m : nactive) {
dp[m] = ndp[m];
sum[m] = nsum[m];
if (dp[m] || sum[m]) active.push_back(m);
}
};
for (int r = 0; r < n; r++) {
fill(dp.begin(), dp.end(), 0);
fill(sum.begin(), sum.end(), 0);
active.clear();
applyBranch(vector<int>{1});
for (int nb : g[r]) {
vector<int> cnt(n + 1, 0);
int mx = 0;
for (int v = 0; v < n; v++) {
if (dist[nb][v] + 1 == dist[r][v]) {
cnt[dist[r][v]]++;
mx = max(mx, dist[r][v]);
}
}
applyBranch(makeWays(cnt, mx));
}
for (int m : active) ans = addmod(ans, sum[m]);
}
for (auto [a, b] : edges) {
vector<int> cntA(n + 1, 0), cntB(n + 1, 0);
int mxA = 0, mxB = 0;
for (int v = 0; v < n; v++) {
if (dist[a][v] < dist[b][v]) {
cntA[dist[a][v]]++;
mxA = max(mxA, dist[a][v]);
} else {
cntB[dist[b][v]]++;
mxB = max(mxB, dist[b][v]);
}
}
vector<int> waysA = makeWays(cntA, mxA);
vector<int> waysB = makeWays(cntB, mxB);
for (int x = 0; x <= mxA; x++) if (waysA[x]) {
for (int y = 0; y <= mxB; y++) if (waysB[y]) {
ans = addmod(ans, mulmod(mulmod(waysA[x], waysB[y]), x + 1 + y));
}
}
}
cout << ans << '\n';
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MOD = 1000000007;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int addmod(int a, int b) {
a += b;
if (a >= MOD) a -= MOD;
return a;
}
int submod(int a, int b) {
a -= b;
if (a < 0) a += MOD;
return a;
}
int mulmod(ll a, ll b) {
return int(a * b % MOD);
}
int main() {
setIO();
int T;
cin >> T;
const int NMAX = 5000;
vector<int> pw2(NMAX + 1);
pw2[0] = 1;
for (int i = 1; i <= NMAX; i++) pw2[i] = addmod(pw2[i - 1], pw2[i - 1]);
while (T--) {
int n;
cin >> n;
vector<vector<int>> g(n);
vector<pair<int, int>> edges;
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
--u; --v;
g[u].push_back(v);
g[v].push_back(u);
edges.push_back({u, v});
}
vector<vector<int>> dist(n, vector<int>(n, -1));
for (int s = 0; s < n; s++) {
queue<int> q;
dist[s][s] = 0;
q.push(s);
while (!q.empty()) {
int v = q.front();
q.pop();
for (int to : g[v]) {
if (dist[s][to] != -1) continue;
dist[s][to] = dist[s][v] + 1;
q.push(to);
}
}
}
auto makeWays = [&](const vector<int>& cnt, int mx) {
vector<int> ways(mx + 1, 0);
int farther = 0;
for (int d = mx; d >= 0; d--) {
if (cnt[d]) ways[d] = mulmod(submod(pw2[cnt[d]], 1), pw2[farther]);
farther += cnt[d];
}
return ways;
};
int ans = 0;
vector<int> dp(n + 1), ndp(n + 1), sum(n + 1), nsum(n + 1);
vector<int> active, nactive;
auto applyBranch = [&](const vector<int>& ways) {
fill(ndp.begin(), ndp.end(), 0);
fill(nsum.begin(), nsum.end(), 0);
nactive.clear();
auto push = [&](int i) {
if (ndp[i] == 0 && nsum[i] == 0) nactive.push_back(i);
};
for (int m : active) {
push(m);
ndp[m] = addmod(ndp[m], dp[m]);
nsum[m] = addmod(nsum[m], sum[m]);
}
for (int x = 0; x < (int)ways.size(); x++) {
int w = ways[x];
if (!w) continue;
push(x);
ndp[x] = addmod(ndp[x], w);
for (int m : active) {
int nm = min(m, x);
int waysAdd = mulmod(dp[m], w);
int sumAdd = addmod(mulmod(sum[m], w), mulmod(waysAdd, m + x));
push(nm);
ndp[nm] = addmod(ndp[nm], waysAdd);
nsum[nm] = addmod(nsum[nm], sumAdd);
}
}
for (int m : active) dp[m] = sum[m] = 0;
active.clear();
for (int m : nactive) {
dp[m] = ndp[m];
sum[m] = nsum[m];
if (dp[m] || sum[m]) active.push_back(m);
}
};
for (int r = 0; r < n; r++) {
fill(dp.begin(), dp.end(), 0);
fill(sum.begin(), sum.end(), 0);
active.clear();
applyBranch(vector<int>{1});
for (int nb : g[r]) {
vector<int> cnt(n + 1, 0);
int mx = 0;
for (int v = 0; v < n; v++) {
if (dist[nb][v] + 1 == dist[r][v]) {
cnt[dist[r][v]]++;
mx = max(mx, dist[r][v]);
}
}
applyBranch(makeWays(cnt, mx));
}
for (int m : active) ans = addmod(ans, sum[m]);
}
for (auto [a, b] : edges) {
vector<int> cntA(n + 1, 0), cntB(n + 1, 0);
int mxA = 0, mxB = 0;
for (int v = 0; v < n; v++) {
if (dist[a][v] < dist[b][v]) {
cntA[dist[a][v]]++;
mxA = max(mxA, dist[a][v]);
} else {
cntB[dist[b][v]]++;
mxB = max(mxB, dist[b][v]);
}
}
vector<int> waysA = makeWays(cntA, mxA);
vector<int> waysB = makeWays(cntB, mxB);
for (int x = 0; x <= mxA; x++) if (waysA[x]) {
for (int y = 0; y <= mxB; y++) if (waysB[y]) {
ans = addmod(ans, mulmod(mulmod(waysA[x], waysB[y]), x + 1 + y));
}
}
}
cout << ans << '\n';
}
return 0;
}