UVa 1664 Conquer a New Region

版权声明:转载注明出处,部分带(坑)文章谢绝转载。 https://blog.csdn.net/linjiayang2016/article/details/81984154

题意

给定一棵有 n 个节点的树,每条边有权值 C ( i , j ) 。任意两个点的容量 S ( i , j ) 定义为 i j 唯一通路上容量的最小值,找一个点,使得它到其他所有点的容量之和最大。

题解

先假设所有点孤立,再逐渐加入每条边。
把边权从大到小排序,加入边时,所有点经过当前边到答案节点的容量一定为这条边的边权。
考虑带权并查集,根节点维护集合大小 n u m [ x ] 以根节点为答案节点的最大容量之和 v a l [ x ]
考虑边 ( u , v ) e f ( x ) 表示 x 所在集合的祖先,由于在同一个集合拥有最优子结构的特点,而 C ( u , v ) 是当前边权中最小的,故 y 集合的任意一个节点到 x 集合的任一节点的容量都不会大于 C ( u , v ) ,合并两个集合后,答案节点必然为 f ( x ) f ( y )
那到底选 f ( x ) 还是 f ( y ) 呢?
当选 f ( x ) 作为根节点时, x 集合的容量之和不变 y 集合的容量之和为 C ( u , v ) ,故总容量之和为

n u m [ f ( y ) ] w + v a l [ f ( x ) ]

当选 f ( y ) 作为根节点时, y 集合的容量之和不变 x 集合的容量之和为 C ( u , v ) ,故总容量之和为

n u m [ f ( x ) ] w + v a l [ f ( y ) ]

因此只需要判断一下那个作为根节点更优即可。

代码

#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;
}

猜你喜欢

转载自blog.csdn.net/linjiayang2016/article/details/81984154