【ybtoj 高效进阶 3.2】【最小生成树】 构造完全图
题目
解题思路
最小生成树就是在一个完全图里找到能使所有点相连且边权最小
那么最小生成树中的一条边可以看作是连接两个大小为V1,V2的连通块中最短的边
而完全图是所有节点都两两相连
那么还需要连V1*V2-1条边
为了边权最小且都大于最小生成树中的那一边d
我们将ta视为d+1
按边权大小排序
查找每条边连接的点是否在同一集合
不是就补边累计答案
且合并两个集合
感觉像并查集了
(⊙_⊙)?
代码
#include<algorithm>
#include<iostream>
#include<cstdio>
using namespace std;
struct lzf{
int x,y,q;
}a[100100];
long long n,tot,fa[100010],num[100010];
bool cmp(lzf f,lzf y)
{
return f.q<y.q;
}
int find(int w)
{
if (fa[w]==w) return w;
else return fa[w]=find(fa[w]);
}
int main()
{
scanf("%lld",&n);
for (int i=1;i<n;i++)
scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].q);
for (int i=1;i<=n;i++) fa[i]=i,num[i]=1;
sort(a+1,a+n,cmp); //按边权排序
for (int i=1;i<n;i++)
{
int xx=find(a[i].x),yy=find(a[i].y);
if (xx!=yy)
{
fa[xx]=yy; //合并
tot+=(num[xx]*num[yy]-1)*(a[i].q+1)+a[i].q; //累计答案,不要忘记加最小生成树中的那一边
num[yy]+=num[xx]; //集合元素增加
}
}
printf("%lld",tot);
return 0;
}