HDU 5176

这道题以前好像在哪遇到过。

注意树的每一条边都是桥,所以,桥两端的点要到达对方是必须通过这条边的。于是,可以把边由小到大排序,利用并查集,这样,每加一条边就连通了一部分,而随着权值的增大,必定是桥两端到达对方经过的最大的边。于是总的权为左边集合数*右边集合数*桥的权值,就可以求出最大值和了。求最小值和相同。两者相减即为结果。

#include <cstdio> 
#include <iostream> 
#include <cstring>  
#include <cctype>  
#include <algorithm>  
#define LL unsigned __int64
using namespace std; 

const int N=150100;

int pre[N];
LL cnt[N];
struct Edge{
	int u,v;
	LL c;
}edge[N];
bool cmp1(Edge a,Edge b){
	if(a.c>b.c) return true;
	return false;
}
bool cmp2(Edge a,Edge b){
	if(a.c<b.c) return true;
	return false;
}

int n;

int findr(int u){
	int r,t=u;
	while(pre[t]!=-1){
		t=pre[t];
	}
	r=t;
	while(u!=r){
		t=pre[u];
		pre[u]=r;
		u=t;
	}
	return r;
}

int main(){
	int t=0;
	while(scanf("%d",&n)!=EOF){
		for(int i=0;i<n-1;i++){
			scanf("%d%d%I64u",&edge[i].u,&edge[i].v,&edge[i].c);
		}
		for(int i=1;i<=n;i++){
			pre[i]=-1,cnt[i]=1;
		}
		LL ans_max=0,ans_min=0;
		int u,v;
		sort(edge,edge+n-1,cmp2);
		for(int i=0;i<n-1;i++){
			u=findr(edge[i].u),v=findr(edge[i].v);
			ans_max+=(cnt[u]*cnt[v]*edge[i].c);
			if(cnt[u]>cnt[v]){
				pre[v]=u;
				cnt[u]+=cnt[v];
			}
			else {
				pre[u]=v;
				cnt[v]+=cnt[u];
			}
		}
		for(int i=1;i<=n;i++){
			pre[i]=-1,cnt[i]=1;
		}
		for(int i=n-2;i>=0;i--){
			u=findr(edge[i].u),v=findr(edge[i].v);
			ans_min+=(cnt[u]*cnt[v]*edge[i].c);
			if(cnt[u]>cnt[v]){
				pre[v]=u;
				cnt[u]+=cnt[v];
			}
			else {
				pre[u]=v;
				cnt[v]+=cnt[u];
			}
		}
		printf("Case #%d: %I64u
",++t,ans_max-ans_min);
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/jie-dcai/p/4323787.html