【HZOI】 Tree

Description

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

Input

第一行N,(2 <= N <= 10^5)表示树T的点数。 
接下来N-1行,Si Ti Di 描述一条边(Si,Ti)权值为 Di。 
保证输入数据构成一棵树。

Output

一行一个数,表示最小的图G的边权和。

Sample Input

4 
1 2 1 
1 3 1 
1 4 2

Sample Output

12

Hint

N≤100000,1≤Di≤100000


【分析】

        这道题目跟Kruskal有关,因为Kruskal的方法是每次将两块独立的图由一条最小的边连接起来
        所以,连通这两块图的 其它的边的数量=(cnt[fx]*cnt[fy]-1) 

            即两个集合的点数乘积,减去正在讨论的这条边。

        而 其他边的值=edge[i].z(最小边的值)+1,那么我们将题目中给出的边排序,然后从小到大每次加入一条边,用并查集找到它们的所属集合,
        再将 ans+=(cnt[fx]*cnt[fy]-1)*(edge[i].z+1);(fx,fy表示边的两端点所属集合)
        然后 father[fx]=fy;cnt[fy]+=cnt[fx];(合并两个集合)
(注:存边和答案要用long long,不然会WA)


【代码】

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctime>
#include<iostream>
#include<algorithm>
using namespace std;
struct wjj{
	long long x,y,z;     
}edge[100005];    //边  注意用long long 
int N,father[100005];    //并查集记录所属集合 
long long cnt[100005];   //记录每个集合的点的数量 
long long ans;
int _getfather(int x)   //带路径压缩的并查集 
{
	if(x!=father[x]) father[x]=_getfather(father[x]);
	return father[x];
}
void _in(long long &x)    //输入优化 
{
	char t=getchar();
	while(t<'0'||'9'<t) t=getchar();
	for(x=t-'0',t=getchar();'0'<=t&&t<='9';x=x*10+t-'0',t=getchar());
}
void _init()
{
	scanf("%d",&N);
	for(int i=1;i<N;i++)
	{
		_in(edge[i].x);
		_in(edge[i].y);
		_in(edge[i].z);
		ans+=edge[i].z;     //注意输出的是完全图的权值和,要算树T中的 
	}
	for(int i=1;i<=N;i++)
	{
		father[i]=i;   //初始化并查集 
		cnt[i]=1;
	}
}
void _qst_z(int l,int r)
{
	int i=l,j=r,mz=edge[(i+j)>>1].z;
	while(i<=j)
	{
		while(edge[i].z<mz) i++;
		while(edge[j].z>mz) j--;
		if(i<=j)
		{
			swap(edge[i],edge[j]);
			i++;j--;
		}
	}
	if(l<j) _qst_z(l,j);
	if(i<r) _qst_z(i,r);
}
void _solve()
{
	int fx,fy;
	_qst_z(1,N-1);     //排序 
	for(int i=1;i<N;i++)
	{
	    fx=_getfather(edge[i].x);
		fy=_getfather(edge[i].y);
		ans+=(cnt[fx]*cnt[fy]-1)*(edge[i].z+1);   //计算答案 
		father[fx]=fy;    //合并集合 
		cnt[fy]+=cnt[fx];	
	}
	printf("%I64d\n",ans);
}
int main()
{
    _init();
    _solve();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值