转载请注明出处,谢谢http://blog.youkuaiyun.com/acm_cxlove/article/details/7854526 by---cxlove
题目:给出一棵树,找出一个点,求出所有点到这个点的权值和最大,权值为路径上所有边权的最小值。
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3659
比赛的时候卡了,一直往DP上想。
结果并查集搞定。
按边排序,从大到小插入,每条边将两个集合连起来,而新加的边是两个集合所有边最小的,那么两个集合中的点交叉的通路最小的边就是新加的,那只要枚举两个集合,a,b是a并入b更优还是b并入a更优就行了。集合内部点已经计算出,相互的只要知道集合中元素的个数就好了。
所以并查集只需要维护一个集合的元素个数,一个集合的总权值
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 200005
#define MOD 1000000007
#define LL long long
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
struct Node
{
int u,v,w;
bool operator< (const Node n1) const
{
return w>n1.w;
}
}edge[N];
int cnt[N],pre[N];
LL sum[N];
int find(int a)
{
return pre[a]=(a==pre[a]?a:find(pre[a]));
}
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
for(int i=0;i<n-1;i++)
{
scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
}
sort(edge,edge+n-1);
for(int i=1;i<=n;i++)
{
sum[i]=0;
pre[i]=i;
cnt[i]=1;
}
LL ans=0;
for(int i=0;i<n-1;i++)
{
int ra=find(edge[i].u);
int rb=find(edge[i].v);
LL atob=(LL)cnt[ra]*edge[i].w+sum[rb];
LL btoa=(LL)cnt[rb]*edge[i].w+sum[ra];
if(atob>btoa)
{
pre[ra]=rb;
cnt[rb]+=cnt[ra];
sum[rb]=atob;
}
else
{
pre[rb]=ra;
cnt[ra]+=cnt[rb];
sum[ra]=btoa;
}
ans=max(ans,max(atob,btoa));
}
printf("%lld\n",ans);
}
return 0;
}