题意:
给出n个点和m条边,每条边有一个权值,题目保证每条边的权值都不一样,求出一个权值最小生成树,并且求出任意选择n个点中的两点之间距离的权值的最小期望。
思路:
最小生成树没什么好说的,因为每条边权值不一样,所以可以保证求出的最小生成树一定只有一个解。按照求出的最小生成树建一棵树,所谓期望=任意两点之间的距离和/C(n,2),这样只要求出任意选择两个点的所有情况的距离和即可,dp一下。
代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 10;
const int MAXM = 1e6 + 10;
struct Edge {
int u, v, w;
bool operator < (const Edge &rhs) const {
return w < rhs.w;
}
} e[MAXM];
struct node {
int v, w;
};
int n, m;
int pa[MAXN];
vector <node> tree[MAXN];
void init() {
for (int i = 1; i <= n; i++) {
pa[i] = i;
tree[i].clear();
}
}
int Find(int x) {
return x == pa[x] ? x : pa[x] = Find(pa[x]);
}
long long kruskal() {
init();
long long res = 0;
sort(e + 1, e + 1 + m);
for (int i = 1; i <= m; i++) {
int pu = Find(e[i].u), pv = Find(e[i].v);
if (pu != pv) {
pa[pu] = pv;
int u = e[i].u, v = e[i].v, w = e[i].w;
tree[u].push_back((node){v, w});
tree[v].push_back((node){u, w});
res += w;
}
}
return res;
}
int sum[MAXN];
double expe = 0;
int dfs(int u, int pre) {
sum[u] = 1;
int cnt = tree[u].size();
for (int i = 0; i < cnt; i++) {
int v = tree[u][i].v, w = tree[u][i].w;
if (v == pre) continue;
sum[u] += dfs(v, u);
expe += (double)sum[v] * (n - sum[v]) * w;
}
return sum[u];
}
int main() {
//freopen("in.txt", "r", stdin);
int T;
scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++)
scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);
printf("%I64d", kruskal());
expe = 0;
dfs(1, -1);
double tmp = (double)n * (n - 1) / 2.0;
printf(" %.2f\n", expe / tmp);
}
return 0;
}