来源:JZOJ
题目描述
对于完全图 ,若有且仅有一棵最小生成树为 ,则称完全图 是树 扩展出的。
给你一棵树 ,找出 能扩展出的边权和最小的完全图 。
解题思路
- 这道题的思路很巧妙,需要好好地动一动脑筋,但其实还是一道 最小生成树的模板题,用边表存储图,首先还是按点权从小到达排序, 表示以 为根节点的树中点的个数,然后可以得出:
- ( 表示边的起点, 表示终点, 表示边权)
代码君
#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;
}