1. 程式人生 > >[最小生成樹][並查集]JZOJ 2940 生成輸入數據

[最小生成樹][並查集]JZOJ 2940 生成輸入數據

return fieldset sam one code 我們 scanf 忽略 event

Description

首先看到題目別太開心,這題可不是讓你出數據~^_*

背景神馬的就忽略了。這題就是給你一棵帶邊權的樹,然後這棵樹是某個完全圖唯一的最小生成樹。問原來的完全圖中所有邊可能的最小邊權和是多少。

完全圖是任意兩個點之間都有邊相連的圖。


Input

第一行包含一個整數T表示數據組數。

每組數據第一行一個整數N表示點數。

接下來N-1行每行三個整數ai,bi,wi表示最小生成樹上ai和bi之間有一條權值為wi的邊。


Output

輸出應有T行,每行表示一組數據的答案。


Sample Input

2
3
1 2 4
2 3 7
4
1 2 1
1 3 1
1 4 2

Sample Output

19
12

Data Constraint

Hint

20%的數據滿足:T≤5,n≤5,wi≤5

另外30%的數據滿足:n≤1000,給定的樹是一條鏈

100%的數據滿足:T≤10,n≤20000,wi≤10000


By moreD

分析

題目和題面無關系列

考場的時候不知道為什麽總覺得可以有奇怪的方法,打了一波計算用set維護然後20分WA

事實上只用模仿生成最小生成樹的過程即可

我們合並兩棵樹時,我們可以確定其中必定只有一條邊是最小生成樹內的,需要添加的邊數=兩棵樹大小的乘積-1,邊的權值為最小生成樹樹邊的權值+1

然後再加上原樹總值就算出來了

技術分享圖片
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=2e4+10;
struct Edge {
    int u,v,w;
}g[N];
int f[N],sz[N];
int T,n;

int Get_F(int x) {return f[x]==x?x:f[x]=Get_F(f[x]);} bool CMP(Edge a,Edge b) { return a.w<b.w; } int main() { for (scanf("%d",&T);T;T--) { scanf("%d",&n); for (int i=1;i<n;i++) scanf("%d%d%d",&g[i].u,&g[i].v,&g[i].w),sz[i]=1,f[i]=i; f[n]=n;sz[n]=1;long long ans=0; sort(g+1,g+n,CMP); for (int i=1;i<n;i++) { int fu=Get_F(g[i].u),fv=Get_F(g[i].v); if (fu!=fv) { ans+=1ll*(sz[fu]*sz[fv]-1ll)*(g[i].w+1ll); sz[fu]+=sz[fv]; f[fv]=fu; ans+=g[i].w; } } printf("%lld\n",ans); } }
View Code

[最小生成樹][並查集]JZOJ 2940 生成輸入數據