HDU - 5723 E - Abandoned country——最小生成树+dfs求任意两点距离的和

题意:有n个村庄,m条道路,要求寻找n个村庄的最小生成树,并且求出最小生成树中任意两点距离的期望

思路:最小生成树用模板生成,因为选中任意一对点的概率都是相同的,所以期望就是任意两点距离的和除以总情况数n*(n-1)/2

求任意两点间距离的和暴力循环O(n^2)的话会超时,可以用dfs,任意两点间距离的和其实是【最小生成树中所有边被访问次数*该边的权】的和。

要求被访问次数,可以先求以该边一节点为根节点的子树的节点总数num(包括这一节点,不包括该边的另一节点, 这里有点绕,参考下面例子),想要到达num个子节点就必须通过该边num次,同理该边的另一节点的子节点数一定是n-num,要通过改变n-num次,所以改变被通过次数时num*(n-num)次,推广到所有边,求和即可。

关于比较绕口的那句话举例:

在纸上画一棵树:

1 2

2 3

2 4

边1——2中:

以2为根节点的子树:

2 3

2 4

共有3个节点,要想达到这三个节点需要通过边1——2三次(对应点12、 13 、 14之间的距离)

以1位为根节点的子树只有1(这里一定是n-num次,仔细想一想),,所以只有一个节点,想要由其子节点到达边1——2只需要通过边1——2一次

所以边1——2一共被访问了1*3 = 3次

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

typedef long long ll;
typedef pair<int, int> P;

const int maxn = 100000 + 10;
const int maxm = 1000000 + 10;
int n, m, par[maxn], num[maxn], vis[maxn];
double ans;

struct Edge {
    int from, to, cost;
    bool operator < (const Edge &e) {
        return cost < e.cost;
    }
}edge[maxm];

vector<P> g[maxn];

void init() {
    for (int i = 0; i <= n; i++) {
            par[i] = i;
            g[i].clear();
        }
        memset(num, 0, sizeof(num));
        memset(vis, 0, sizeof(vis));
        ans = 0.0;
}

int seek(int x) { return par[x] == x ? x : par[x] = seek(par[x]); }

void dfs(int cur) {
    num[cur] = 1;
    vis[cur] = 1;
    for (int i = 0; i < g[cur].size(); i++) {
        int son = g[cur][i].first;
        if (vis[son]) continue;
        dfs(son);
        num[cur] += num[son];
        ans += 1.0 * num[son] * (n - num[son]) * g[cur][i].second;
    }
}

int main() {
    int T; scanf("%d", &T);
    while (T--) {
        scanf("%d %d", &n, &m);
        init();
        for (int i = 0; i < m; i++) {
            scanf("%d %d %d", &edge[i].from, &edge[i].to, &edge[i].cost);
        }
        sort(edge, edge + m);
        ll sum = 0, cnt = 0;
        for (int i = 0; i < m && cnt < n - 1; i++) {
            int f = edge[i].from, t = edge[i].to, c = edge[i].cost;
            int x = seek(f), y = seek(t);
            if (x != y) {
                par[x] = y;
                sum += c;
                cnt++;
                g[f].push_back(make_pair(t, c));
                g[t].push_back(make_pair(f, c));
            }
        }
        dfs(1);
        printf("%lld %.2lf\n", sum, ans/(1.0 * n * (n - 1) / 2));
    }
    return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值