HDU 5723 Abandoned country(最小生成树+DFS)

题意
t(t≤10)组数据,n(n≤1e5)个点,m(m≤1e6)条边,每条边长为w(w≤1e6),每条边的长度都不相等。求最小生成树的总长度及最小生成树上任意两点的距离期望值(保留两位小数)。

样例输入
1
4 6
1 2 1
2 3 2
3 4 3
4 1 4
1 3 5
2 4 6

样例输出
6 3.33

思路
由于每条边的长度都不相等,所以最小生成树唯一。先用Kruscal求出最小生成树,再通过DFS算出最小生成树上每条边经过的次数,每条边经过的次数等于这条边的左边的点的个数乘以右边的点的个数。最后再由各个边经过的次数乘以各边的长度之和,除以n*(n-1)/2即为树上任意两点的距离期望值。

注意点
1. 求和的时候要用long long int。

 1 #include <cstdio>
 2 #include <string.h>
 3 #include <algorithm>
 4 using namespace std;
 5 typedef long long int LL;
 6 const int N = 1e5+10, M = 1e6+10, N2= 2e5+20;
 7 int u[M], v[M], edge[M], dad[N], nu[N2], nv[N2], first[N2], Next[N2], cnt[N] = {0};
 8 LL w[M], nw[N2], sum = 0;
 9 bool cmp(const int a, const int b){
10     return w[a] < w[b];
11 }
12 int FindDad(int x){
13     return dad[x] == x ? x : dad[x] = FindDad(dad[x]);
14 }
15 LL Kruscal(int n)
16 {
17     int x, a, b, da, db;
18     memset(first, -1, sizeof(first));
19     LL ans = 0;
20     for(int i=1; i<=n; ++i)
21         dad[i] = i;
22     for(int i=0, t=1, num=0; t<n; ++i)
23     {
24         x = edge[i];
25         a = u[x];
26         b = v[x];
27         da = FindDad(a);
28         db = FindDad(b);
29         if(da - db)
30         {
31             dad[b] = dad[db] = da;
32             ans += w[x];
33 
34             nu[num] = --a, nv[num] = --b, nw[num] = w[x];
35             Next[num] = first[a];
36             first[a] = num++;
37 
38             nu[num] = b, nv[num] = a, nw[num] = w[x];
39             Next[num] = first[b];
40             first[b] = num++;
41 
42             ++t;
43         }
44     }
45     return ans;
46 }
47 void DFS(int n, int from, int to)
48 {
49     ++cnt[from];
50     for(int i=first[from]; ~i; i=Next[i])
51     {
52         if(nv[i] != to)
53         {
54             DFS(n, nv[i], from);
55             cnt[from] += cnt[nv[i]];
56             sum += nw[i] * (n - cnt[nv[i]]) * cnt[nv[i]];
57         }
58     }
59 }
60 int main(void)
61 {
62     int n, m, t;
63     scanf("%d", &t);
64     while(t--)
65     {
66         scanf("%d %d", &n, &m);
67         for(int i=0; i<m; ++i)
68         {
69             scanf("%d %d %lld", u+i, v+i, w+i);
70             edge[i] = i;
71         }
72         sort(edge, edge+m, cmp);
73         printf("%lld ", Kruscal(n));
74         DFS(n, 0, 0);
75         printf("%.2lf
", (double)sum / n / (n-1) * 2);
76         sum = 0;
77         memset(cnt, 0, sizeof(cnt));
78     }
79     return 0;
80 }

后来感觉用Kruscal求最小生成树时间吃得有点紧(3000ms左右),仔细想了想,每条边的长度小于1e6,而且每条边的长度都不相等,那么就可以以边长作为数组下标,这样排序的时间也省了。

 1 #include <cstdio>
 2 #include <string.h>
 3 #include <algorithm>
 4 using namespace std;
 5 typedef long long int LL;
 6 const int N = 1e5+10, MLEN = 1e6+10, N2 = 2e5+20;
 7 int u[MLEN], v[MLEN], dad[N], nu[N2], nv[N2], nw[N2], first[N2], Next[N2], cnt[N];
 8 LL sum;
 9 int FindDad(int x){
10     return dad[x] == x ? x : dad[x] = FindDad(dad[x]);
11 }
12 LL Kruscal(int n, int MIN)
13 {
14     int a, b, da, db, num = 0;
15     LL ans = 0;
16     memset(first, -1, sizeof(first));
17     for(int i=1; i<=n; ++i)
18         dad[i] = i;
19     for(int i=MIN, t=1; t<n; ++i)
20     {
21         a = u[i];
22         b = v[i];
23         if(a)
24         {
25             da = FindDad(a);
26             db = FindDad(b);
27             if(da != db)
28             {
29                 ans += i;
30                 dad[b] = dad[db] = da;
31 
32                 nu[num] = --a, nv[num] = --b, nw[num] = i;
33                 Next[num] = first[a];
34                 first[a] = num++;
35 
36                 nu[num] = b, nv[num] = a, nw[num] = i;
37                 Next[num] = first[b];
38                 first[b] = num++;
39                 ++t;
40             }
41         }
42     }
43     return ans;
44 }
45 void DFS(int n, int from, int to)
46 {
47     ++cnt[from];
48     for(int i=first[from]; ~i; i=Next[i])
49     {
50         if(nv[i] != to)
51         {
52             DFS(n, nv[i], from);
53             cnt[from] += cnt[nv[i]];
54             sum += (LL)nw[i] * cnt[nv[i]] * (n - cnt[nv[i]]);
55         }
56     }
57 }
58 int main(void)
59 {
60     int t, n, m, a, b, w, MIN;
61     scanf("%d", &t);
62     while(t--)
63     {
64         scanf("%d %d", &n, &m);
65         MIN = MLEN;
66         memset(u, 0, sizeof(u));
67         for(int i=0; i<m; ++i)
68         {
69             scanf("%d %d %d", &a, &b, &w);
70             u[w] = a, v[w] = b;
71             MIN = w < MIN ? w : MIN;
72         }
73         printf("%lld ", Kruscal(n, MIN));
74         memset(cnt, 0, sizeof(cnt));
75         sum = 0;
76         DFS(n, 0, 0);
77         printf("%.2lf
", (double)sum / n / (n-1) * 2);
78     }
79 }
原文地址:https://www.cnblogs.com/corvey/p/5698901.html