AcWing 346. 走廊泼水节 题解

前言

这道题A了之后莫名其妙就想打个题解

题目

AcWing 346. 走廊泼水节

给定一棵 N 个节点的树,要求增加若干条边,把这棵树扩充为完全图,并满足图的唯一最小生成树仍然是这棵树。
求增加的边的权值总和最小是多少。
注意: 树中的所有边权均为整数,且新加的所有边权也必须为整数。

分析

这道题最后要求最终生成的完全图的最小生成树是给定的原树,我们很容易想到类似Kruskal的想法。首先把边按照从小到大进行排序,每次对于给定的一条边而言,因为这条边要成为最小生成树上的边,所以这条边所连接的两个连通块内的其他边都要比这条边大,要让增加的权值总和最小我们只要让其他边的权值都比这条边的权值大1就OK啦。
为什么要从小到大排序呢?我们可以考虑Kruskal的过程,如果这条边是当前剩下的连通两个连通块的所有边中的最小边,那么它一定在最小生成树中。我们考虑本题的过程,如果边是从小到大枚举的话,那么显然剩下还没连接的部分的连通块里面的边一定比当前这个边要大。所以从小到大枚举可以保证结果的正确性。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
int father[6010];
int sze[6010];
ll ans;
struct st
{
	int x,y,val;
} side[6010];
int getfather(int x)
{
	if(father[x]==x) return x;
	return father[x]=getfather(father[x]);
}
void merge(int x,int y,int val)
{
	int fx=getfather(x);
	int fy=getfather(y);
	if(fx!=fy)
	{
		ans+=1LL*(1LL*sze[fx]*sze[fy]-1)*(val+1);
		sze[fy]+=sze[fx];
		sze[fx]=0;
		father[fx]=fy;
	}
}
bool cmp(st r1,st r2)
{
	return r1.val<r2.val;
}
void init()
{
	ans=0;
	scanf("%d",&n);
	for(int i=1;i<n;i++)
		scanf("%d%d%d",&side[i].x,&side[i].y,&side[i].val);
	sort(side+1,side+n,cmp);
	for(int i=1;i<=n;i++)
	{
		sze[i]=1;
		father[i]=i;
	}
}
void work()
{
	for(int i=1;i<n;i++)
	{
		merge(side[i].x,side[i].y,side[i].val);
	}
}
void print()
{
	printf("%lld\n",ans);
}
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		init();
		work();
		print();
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值