版权声明:转载注明出处,部分带(坑)文章谢绝转载。 https://blog.csdn.net/linjiayang2016/article/details/81984154
题意
给定一棵有 个节点的树,每条边有权值 。任意两个点的容量 定义为 到 唯一通路上容量的最小值,找一个点,使得它到其他所有点的容量之和最大。
题解
先假设所有点孤立,再逐渐加入每条边。
把边权从大到小排序,加入边时,所有点经过当前边到答案节点的容量一定为这条边的边权。
考虑带权并查集,根节点维护集合大小
和以根节点为答案节点的最大容量之和
。
考虑边
,
表示
所在集合的祖先,由于在同一个集合拥有最优子结构的特点,而
是当前边权中最小的,故
集合的任意一个节点到
集合的任一节点的容量都不会大于
,合并两个集合后,答案节点必然为
或
。
那到底选
还是
呢?
当选
作为根节点时,
集合的容量之和不变
集合的容量之和为
,故总容量之和为
当选
作为根节点时,
集合的容量之和不变
集合的容量之和为
,故总容量之和为
因此只需要判断一下那个作为根节点更优即可。
代码
#include<bits/stdc++.h>
using namespace std;
#define maxn 200010
struct edge{
int u,v,w;
bool operator<(const edge &e)const{
return w>e.w;
}
}e[maxn];
int n,fa[maxn],num[maxn];
long long val[maxn];
int find(int x){
if(fa[x]==x)
return x;
return fa[x]=find(fa[x]);
}
void Union(int u,int v,int w){
int fx=find(u),fy=find(v);
if((long long)num[fx]*w+val[fy]>(long long)num[fy]*w+val[fx])
swap(fx,fy);//简化代码
fa[fy]=fx;
val[fx]+=(long long)num[fy]*w;//维护答案
num[fx]+=num[fy];//维护大小
}
int main(void)
{
while(~scanf("%d",&n)){
for(int i=1;i<=n;i++)
fa[i]=i,num[i]=1,val[i]=0;
for(int i=1;i<n;i++)
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
sort(e+1,e+n);
for(int i=1;i<n;i++)
Union(e[i].u,e[i].v,e[i].w);
printf("%lld\n",val[find(1)]);
//原图为一棵树,所有节点必然连载一起,根节点为1
}
return 0;
}