题目大意:有n(n<100000)个地点面m(m<1000000)条路,修每条路都有花费w,求最小花费使每个地点能够互相到达并求出人一两点的花费期望;
因为n很大所以用kruskal求最小生成树求出最小花费,然后dfs搜索回溯的办法找到所有情况每条路用过的次数并求出总花费,用总花费除以所有可能发生的次数(n*(n-1)/2)就是我们要求的期望
代码引用了网友的。前向星存图,克鲁斯卡尔算法求MST(肯定得用并查集啦,先对边按权值弱序化排序)
#define _CRT_SECURE_NO_WARNINGS
#include<cstdio>
#include <algorithm>
#include <cstring>
const int N = 1e5 + 15;
using namespace std;
int n, m, father[N], head[N], tot, cnt[N], v[N];
typedef long long LL;
struct node {
int x, y, v;
bool operator < (const node&p)const {
return v < p.v;
}
}p[N * 10];
struct Edges {
int to, next, val;
}e[N << 1];
void add(int u, int v, int val) {
e[tot].to = v;
e[tot].val = val;
e[tot].next = head[u];
head[u] = tot++;
}
int find(int x) {
return x == father[x] ? x : father[x] = find(father[x]);
}
void init(int n) {
for (int i = 1; i <= n; i++) father[i] = i;
memset(head, -1, sizeof head);
memset(v, 0, sizeof v);
tot = 0;
}
void dfs(int x, int fa) {
cnt[x] = 1;
for (int i = head[x]; i != -1; i = e[i].next) {
int to = e[i].to;
if (fa == to) continue;
v[to] = e[i].val;//存权值
dfs(to, x);
cnt[x] += cnt[to];
}
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &m);
init(n);
for (int i = 0; i<m; i++) {
scanf("%d%d%d", &p[i].x, &p[i].y, &p[i].v);
}
sort(p, p + m); //按权值排序从小到大排序
LL sum = 0;
for (int i = 0; i < m; i++) { //克鲁斯卡尔算法
int xx = find(p[i].x), yy = find(p[i].y);
if (xx != yy) {
add(p[i].x, p[i].y, p[i].v); //造最小生成树
add(p[i].y, p[i].x, p[i].v);
father[xx] = yy;
sum += 1LL * p[i].v;
}
}
dfs(1, -1);
LL x = 0, y = 1ll * n*(n - 1) / 2ll;
for (int i = 2; i <= n; i++) {
x += 1ll * v[i] * cnt[i] * (n - cnt[i]);
}
printf("%I64d %.2f\n", sum, 1.0*x / y);
}
return 0;
}
\
\