LOJ10067构造完全图【克鲁斯卡尔】

题目描述
对于完全图 ,若有且仅有一棵最小生成树为 ,则称完全图 是树 扩展出的。

给你一棵树 ,找出 能扩展出的边权和最小的完全图 。

输入格式
第一行 表示树 的点数;

接下来 行三个整数 ;描述一条边 权值为 ;

保证输入数据构成一棵树。

输出格式
输出仅一个数,表示最小的完全图 的边权和。

样例
样例输入
4
1 2 1
1 3 1
1 4 2
样例输出
12
样例说明
添加 即可。

数据范围与提示
对于 的数据,;
对于 的数据,;
对于 的数据,。

看到题目范围,可以排除掉prim。那么就是克鲁斯卡尔了。我们考虑克鲁斯卡尔的思想,每次选择最小的一条边。因为是一颗唯一的最小生成树,所以我们就要保证在完全图上执行克鲁斯卡尔的时候每一次都会选择这条边,那么就相当于这两个集合的所有不是最小生成树上的边都必须比这条边大1才能保证唯一性和最小性
如有不理解,可看下图。中途画错了。。稍微修改一下样例

我们在{1},{2}这两个不同的并查集中,找到了一条边(1,2,1)。之所以能找到这条边,是因为这两个集合剩余边都比这条边大(这里无剩余边),连接并合并,答案+1
在这里插入图片描述

同上,我们又选到了(1,4,1),连接并合并,答案+1
在这里插入图片描述

现在来想一下,为什么选了(1,4,1)而不是(2,4)这条边?是因为(2,4)这条边比(1,4,1)要大1,即(2,4,2)才能保证生成树的唯一性

现在我们又选到了(1,3,2)
在这里插入图片描述
同上,之所以不选(4,3)或(2,3)是因为这两条边更大,为了满足最小性,这两条边的长度为3
至此,跑完克鲁斯卡尔,正确性显然
在这里插入图片描述

代码就很好写了,稍微修改一下并查集记录一下节点个数,用乘法原理即OK

#include<bits/stdc++.h>
using namespace std;
int n;
int father[100010];
long long cnt[100010];
struct Node
{
	int a,b;long long dis;
}Edge[100010];
bool cmp(Node a,Node b)
{
	return a.dis<b.dis;
}
int getfather(int x)
{
	return father[x]==x?x:father[x]=getfather(father[x]);
}
long long ans;
int main()
{
	int s,t;long long d;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	father[i]=i,cnt[i]=1;
	for(int i=1;i<n;i++)
	{
		scanf("%d%d%lld",&s,&t,&d);
		Edge[i].a=s,Edge[i].b=t,Edge[i].dis=d;
		ans+=d;
	}
	sort(Edge+1,Edge+n,cmp);
	for(int i=1;i<n;i++)
	{
		int fx=getfather(Edge[i].a),fy=getfather(Edge[i].b);
		ans+=(cnt[fx]*cnt[fy]-1)*(Edge[i].dis+1);//这是化简后的结果 
		father[fy]=fx;
		cnt[fx]+=cnt[fy];
	}
	printf("%lld",ans);
    return 0;
}

谢谢

posted @ 2019-12-22 09:21  最爱丁珰  阅读(50)  评论(0编辑  收藏  举报