//最小生成樹,Kruskal算法 struct rec { int x; int y; int z; }edge[50010]; int fa[10010],n,m,ans; bool operator <(rec a,rec b) { return a.z<b.z; } int get(int x) { if(x==fa[x]) return x; return fa[x]=get(fa[x]); //壓縮路徑,構造並查集 } int main() { cin>>n>>m; for(int i=1;i<=m;i++) scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].z); //按照邊權排序 sort(edge+1,edge+m+1); //並查集初始化 ,一開始初始化的時候每一棵個節點都初始化為一顆以自己為根節點的樹 for(int i=1;i<=n;i++) fa[i]=i; //求最小生成樹 for(int i=1;i<=m;i++) { int x=get(edge[i].x); int y=get(edge[i].y); if(x==y) continue; fa[x]=y; ans+=edhe[i].z; } cout<<ans<<endl; } //給定一顆N個節點的樹,要求增加若干條邊,把這棵樹擴充成為完全圖 //並滿足圖的唯一最小生成樹仍然是這棵樹,求增加的邊的權值總和最小是多少 //一開始我的錯誤思想是先把一顆生成樹給它生成出來 //然后在去找到那條權值最大的邊(max),然后最后在計算出沒有相互連接的結點對數(num) //最后用(num)*max,就計算出權值總和最小是多少了。 //但是這樣做的話相當於沒有用到最小生成樹的算法 //所以正確的做法應該是,在生成最小生成樹的過程當中就把權值總和最小計算出來了, //這樣就使用到了最小生成樹的算法,我們需要在模板代碼的基礎上改動一些地方 //加一個S數組記錄每個森林(即並查集)中的結點個數,這樣不同的森林連接時才知道 //有多少個點對需要相互連接,並且連接的權值應當時了兩個森林連接時的最小邊的權值+1 //從每個森林只有一個結點,擴展到只剩下一個森林,即一顆最小生成樹生成時,則答案就剛好算出來了 struct rec{ int x; int y; int z; }edge[maxn]; int fa[6010],s[6010],n,T; typedef long long l; l ans; bool operator <(rec a,rec b) { return a.z<b.z; } int get(int x) { if(x==fa[x]) return x; return fa[x]=get(fa[x]); } int main() { cin>>T; while(T--) { cin>>n; for(int i=1;i<n;i++) { scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].z); } for(int i=1;i<=n;i++) { fa[i]=i; s[i]=1; //初始化,相當於生成n個森林,每個森林只有一個結點 //一個結點一顆樹。 } sort(edge+1,edge+n); for(int i=1;i<n;i++) { x=get(edge[i].x); y=get(edge[i].y); if(x==y) continue; fa[x]=y; s[y]+=s[x];//x以y作為父節點,做父節點y就應該時這個森林的代表節點 ans+=(l)(edge[i].z+1)*(s[x]*s[y]-1); //連接成最小生成樹的同時,把兩個並查集當中不相互連接的點全部虛擬連接了一個權值為edge[i].z+1的邊 } cout<<ans<<endl; } }