清北学堂模拟赛d6t5 侦探游戏

分析:简化一下题意就是给任意两对点连一条权值为0的边,求出每次连边后最小生成树的权值和*2/(n - 1) * n.

      每次求最小生成树肯定会爆炸,其实每次加边只是会对最小生成树上的一条边有影响,也就是加入这条边权为0的边后形成的环上权值最大的边,我们可以每次在树上倍增找到这条边,可以通过60%的点.

      最小生成树的变形题都有一个思路,就是当我们无法继续优化的时候我们就思考一下克鲁斯卡尔算法的原理,因为每条边是按照边权从小到大加入的,所以在加入这条边的时候,这条边两端连的点对之间连一条权值为0的边,所形成的环上的最大权值的边一定是新加入的这条边,利用乘法原理统计一下点对数就可以了.

#include <bits/stdc++.h>

using namespace std;
int n,m,fa[20010],num[20010];
long long cnt,ans;

struct node
{
    int u,v,w;
}e[100010];

bool cmp(node a,node b)
{
    return a.w < b.w;
}

int find(int x)
{
    if (x == fa[x])
        return x;
    return fa[x] = find(fa[x]);
}

void hebing(int x,int y)
{
     num[x] += num[y];
     num[y] = 0;
     fa[y] = x;
}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i = 1; i <= n; i++)
    {
        fa[i] = i;
        num[i] = 1;
    }
    for (int i = 1; i <= m; i++)
        scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
    sort(e + 1,e + 1 + m,cmp);
    for (int i = 1; i <= m; i++)
    {
        int fu = find(e[i].u),fv = find(e[i].v);
        if (fu != fv)
        {
            ans += e[i].w;
            cnt += 1LL * num[fu] * num[fv] * e[i].w;
            hebing(fu,fv);
        }
    }
    double temp = ans - 2.0 * cnt / n / (n - 1);
    printf("%.2lf",temp);

    return 0;
}

 

posted @ 2017-10-07 20:21  zbtrs  阅读(414)  评论(0编辑  收藏  举报