该题巧妙的运用了并查集,运用了类似于最小生成树算法的过程 ,通过该题可以对并查集有一个更深的理解 。
由于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;
}