JZOJ 构造完全图 (图论-Kruskal)

来源:JZOJ

题目描述

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

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

解题思路

  • 这道题的思路很巧妙,需要好好地动一动脑筋,但其实还是一道 K r u s k a l Kruskal 最小生成树的模板题,用边表存储图,首先还是按点权从小到达排序, s [ i ] s[i] 表示以 i i 为根节点的树中点的个数,然后可以得出: ( s [ v ] + s [ u ] 1 ) ( e [ i ] . v + 1 ) + e [ i ] . v (s[v]+s[u]-1)*(e[i].v+1)+e[i].v
  • v v 表示边的起点, u u 表示终点, e [ i ] . v e[i].v 表示边权)

代码君

#include <bits/stdc++.h>
using namespace std;
int n,father[100010];
long long s[100010];
long long ans=0;
struct node
{
	int x,y;
	long long v;
}e[200010];
bool mycmp(node a,node b)  //按点权排序
{
	return a.v<b.v;
}
void Freopen()
{
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
}
int getfather(int k)  //并查集寻找父亲
{
	if (father[k]==k) return k;
	father[k]=getfather(father[k]);
	return father[k];
}
void Kruskal()  //克鲁斯卡尔
{
	for (int i=1;i<=n;i++) father[i]=i,s[i]=1;  //开始时每个点自己都是一个集合,
	for (int i=1;i<=n-1;i++)
	{
		int v=getfather(e[i].x);
		int u=getfather(e[i].y);
		if (v!=u)  //判断v和u是否在同一个集合中
		{
			
			ans+=((long long)s[v]*s[u]-1)*(e[i].v+1)+e[i].v;  //解题思路中已经给出
			father[v]=u;  //把v和u合并
	        s[u]+=s[v];  //因为u现在成为了v的父亲,所以以u为根节点的树中点个数应加上v
		}
	}
	printf("%lld",ans);
}
void init()
{
	scanf("%d",&n);
	for (int i=1;i<=n-1;i++)
	{
		scanf("%d %d %lld",&e[i].x,&e[i].y,&e[i].v);  //边表
	}
	sort(e+1,e+n,mycmp);  //排序
}
int main()
{
	Freopen();
	init();
	Kruskal();
	return 0;
}
发布了27 篇原创文章 · 获赞 33 · 访问量 1683

猜你喜欢

转载自blog.csdn.net/qq_43081996/article/details/104184509
今日推荐