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\n",++t,ans_max-ans_min); } return 0; }