UVA 1664 - Conquer a New Region(并查集)

该题巧妙的运用了并查集,运用了类似于最小生成树算法的过程 ,通过该题可以对并查集有一个更深的理解 。 

由于i和j唯一通路上容量的最小值为该两点的容量,求一个点到其他所有点的容量最大值 。

首先,解决两点的容量问题 ,我们将所有边从大到小排序,然后从大到小枚举,我们假设根结点就是要找的城市中心点,那么当又加入一条边时,该边的两个顶点所在的集合设为A、B,集合A、B的顶点a、b,要让谁当中心点呢? 易知:无论谁当中心点,它与另一个集合中任一点的容量都为该边长度(因为是从大到小枚举的)。

那么为了求出总容量,我们要维护一些值,用空间换时间 。  维护每个顶点的总容量sum[i],维护每个顶点与之相连的顶点数量,cnt[i],当前答案ans 。

那么对于a、b点,如果以a为中心,总容量为sum[a] + cnt[b] * e[i].c 。 反之亦然,哪个量大,则以哪个点为城市中心,也就是并查集的根结点 。

该题的巧妙之处在于,将答案结点维护成并查集的根结点,快速的找出一个集合中的城市中心 。 

并查集用了路径压缩之后其实已经很快了,没有必要在改变树的高度,因为那样会改变根结点,不仅写起来麻烦,还丢掉了许多很好的特性 。 

该题就是通过这些特性,维护一些重要的量以达到快速求解的目的 。 请读者细细品味 。 

细节参见代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 200000 + 5;
int n,m,p[maxn];
ll sum[maxn],cnt[maxn];
struct Edge{
    ll a,b,c;
    bool operator < (const Edge& rhs) const {
        return c > rhs.c;
    }
}e[maxn];
int findd(int x) { return p[x] == x ? x : p[x] = findd(p[x]); }
ll solve() {
    for(int i=1;i<=n;i++) { p[i] = i ; sum[i] = 0 ; cnt[i] = 1; }
    ll ans = 0;
    sort(e,e+n-1);
    for(int i=0;i<n-1;i++) {
        int x = findd(e[i].a) , y = findd(e[i].b);
        ll suma = sum[x]+cnt[y]*e[i].c,sumb = sum[y]+cnt[x]*e[i].c;
        if(suma < sumb) {
            p[x] = y; sum[y] = sumb;
            cnt[y] += cnt[x]; ans = sum[y];
        }
        else {
            p[y] = x; sum[x] = suma;
            cnt[x] += cnt[y]; ans = sum[x];
        }
    }
    return ans;
}
int main() {
    while(~scanf("%d",&n)) {
        for(int i=0;i<n-1;i++) 
            scanf("%lld%lld%lld",&e[i].a,&e[i].b,&e[i].c);
        printf("%lld\n",solve());
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值