[最小生成树][并查集]JZOJ 2940 生成输入数据

 

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

 

posted @ 2019-01-19 16:40  Vagari  阅读(371)  评论(0编辑  收藏  举报