题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5723
题意:求最小生成树,并且求这棵最小生成树上所有边走过次数的期望。
走过次数的期望=Σ边被走过次数*边权/(n*(n-1)/2)
求边被走过的次数,相当于关心这个边的两侧分别有多少点,走的次数就是两边的点数的乘积这个很好理解。可以从边的一侧开始dfs,找到这个边的一侧点数x,由于最小生成树是联通的,那么边的另一侧点数一定是n-x。所以这条边的贡献是(n-x)*x*w。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long LL; 5 typedef struct Edge { 6 int v, next; 7 LL w; 8 }Edge; 9 10 typedef struct E { 11 int u, v; 12 LL w; 13 }E; 14 const int maxn = 200100; 15 const int maxm = 1000100; 16 int n, m; 17 E e[maxm<<1]; 18 Edge edge[maxm<<1]; 19 int head[maxn], ecnt; 20 int pre[maxn]; 21 LL ret1, ret2; 22 23 int find(int x) { 24 return x == pre[x] ? x : pre[x] = find(pre[x]); 25 } 26 27 bool unite(int x, int y) { 28 x = find(x); y = find(y); 29 if(x != y) { 30 pre[x] = y; 31 return 1; 32 } 33 return 0; 34 } 35 36 void init() { 37 memset(head, -1, sizeof(head)); 38 ecnt = 0; ret1 = 0; ret2 = 0; 39 for(int i = 1; i <= n; i++) pre[i] = i; 40 } 41 42 void adde(int u, int v, LL w) { 43 edge[ecnt].v = v; edge[ecnt].w = w; edge[ecnt].next = head[u]; head[u] = ecnt++; 44 edge[ecnt].v = u; edge[ecnt].w = w; edge[ecnt].next = head[v]; head[v] = ecnt++; 45 } 46 47 bool cmp(E a, E b) { 48 return a.w < b.w; 49 } 50 51 int dfs(int u, int p) { 52 int siz = 1; 53 for(int i = head[u]; ~i; i=edge[i].next) { 54 int v = edge[i].v; LL w = edge[i].w; 55 if(p == v) continue; 56 int pre = dfs(v, u); 57 siz += pre; 58 ret2 += (LL)pre * (LL)(n - pre) * w; 59 } 60 return siz; 61 } 62 63 int main() { 64 //freopen("in", "r", stdin); 65 int T, u, v; 66 LL w; 67 scanf("%d", &T); 68 while(T--) { 69 scanf("%d%d",&n,&m); 70 init(); 71 for(int i = 0; i < m; i++) { 72 scanf("%d%d%lld",&u,&v,&w); 73 e[i].u = u; e[i].v = v; e[i].w = w; 74 } 75 sort(e, e+m, cmp); 76 for(int i = 0; i < m; i++) { 77 u = e[i].u; v = e[i].v; w = e[i].w; 78 if(unite(u, v)) { 79 ret1 += w; 80 adde(u, v, w); 81 } 82 } 83 dfs(1, -1); 84 LL k = n * (n - 1); 85 printf("%lld %.2lf ", ret1, 2.0*ret2/(double)k); 86 } 87 return 0; 88 }