最小完全图(最小生成树加边成完全图)

最小完全图

 时间限制: 1 s
 空间限制: 128000 KB
题目描述 Description

若一个图的每一对不同顶点都恰有一条边相连,则称为完全图。

最小生成树MST在Smart的指引下找到了你,希望你能帮它变成一个最小完全图(边权之和最小的完全图)。

注意:必须保证这个最小生成树MST对于最后求出的最小完全图是唯一的。

输入描述 Input Description

第一行一个整数n,表示生成树的节点数。

接下来有n-1行,每行有三个正整数,依次表示每条边的顶点编号和边权。

(顶点的边号在1-n之间,边权<231)

输出描述 Output Description

一个整数ans,表示以该树为最小生成树的最小完全图的边权之和。

样例输入 Sample Input

4

1 2 1

1 3 1

1 4 2

样例输出 Sample Output

12

数据范围及提示 Data Size & Hint

30%的数据:n<1000;

100%的数据:n≤20000,所有的边权<2^31。

题目链接:http://codevs.cn/problem/2796/


分析Kruskal算法可知,最小生成树中的每一条边,都是连接某两个连通块的所有边中,最小的那一个。因此我们要造一个完全图,连接这两个连通块的所有边都必须大于最小生成树中对应的边。
所以对最小生成数中的边按升序排列,依次维护加边操作和对应连通块中点的个数就行了。
#include<bits/stdc++.h>
#define N 20050
using namespace std;

long long pre[N];
long long num[N]={0};
void init()
{
    for(long long i=0;i<N;i++)pre[i]=i,num[i]=1;
}

long long Find(long long x)
{
    long long boss=x;
    while(pre[boss]!=boss)boss=pre[boss];
    
    while(x!=boss)
    {
        long long now=pre[x];
        pre[x]=boss;
        x=now;
    }
    return boss;
}

struct ss
{
    long long u,v,w;
    
    bool operator < (const ss&s) const
    {
        return w<s.w;
    }
};

ss edge[N];


int main()
{
    long long n;
    init();
    scanf("%lld",&n);
    for(long long i=1;i<n;i++)
    {
        scanf("%lld %lld %lld",&edge[i].u,&edge[i].v,&edge[i].w);
    }
    
    sort(edge+1,edge+n);
    
    long long ans=0;
    for(long long i=1;i<n;i++)
    {
        long long u=edge[i].u,v=edge[i].v;
        ans+=(edge[i].w+1)*(num[Find(u)]*num[Find(v)]-1);
        ans+=edge[i].w;
        num[Find(v)]+=num[Find(u)];
        pre[Find(u)]=Find(v);
    }
    printf("%lld\n",ans);
    return 0;
}
View Code

 

posted @ 2018-09-19 14:11  1371767389  阅读(3163)  评论(3编辑  收藏  举报