CH6201 走廊泼水节 (最小生成树 kruskal 并查集)

走廊泼水节

CH6201

题意

给定一棵N个节点的树,要求增加若干条边,把这棵树扩充为完全图,并满足图的唯一最小生成树仍然是这棵树。求增加的边的权值总和最小是多少。

输入格式

本题为多组数据~
第一行t,表示有t组测试数据
对于每组数据
第一行N,表示水龙头的个数(当然也是OIER的个数);
2到N行,每行三个整数X,Y,Z;表示水龙头X和水龙头Y有一条长度为Z的小道

输出格式

对于每组数据,输出一个整数,表示修建的所有道路总长度的最短值。

样例输入

2
3
1 2 2
1 3 3
4
1 2 3
2 3 4
3 4 5

样例输出

4
17

数据范围与约定

每个测试点最多10组测试数据
50% n<=1500;
100% n<=6000
100% z<=100

样例解释

第一组数据,在2和3之间修建一条长度为4的道路,是这棵树变成一个完全图,且原来的树依然是这个图的唯一最小生成树.

分析:

搞一遍kruskal,扫描到边(u,v,w)时,设u所在集合x,v所在集合y,合并x、y,为了保证(u,v)一定是连接x,y的最短的边,对于x的任意点a,y的任意点b,至少得让边(a,b)的边权为w+1,x和y间一共增加x * y-1条边,答案加上(w+1)*(x * y-1)

我的代码:

#include
#include
#include
typedef long long ll;
const int inf=0x3f3f3f3f;
const int inn=0x80808080;
using namespace std;
const int maxm=6e3+5;
int pre[maxm];
int num[maxm];//记录并查集 集合里点的数量
void init(){
    for(int i=0;i<maxm;i++){
        pre[i]=i;
        num[i]=1;
    }
}
int ffind(int x){
    return pre[x]==x?x:pre[x]=ffind(pre[x]);
}
struct Node{
    int a,b,c;
}a[maxm];
int cmp(Node a,Node b){
    return a.c<b.c;
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        init();
        int n;
        scanf("%d",&n);
        for(int i=0;i<n-1;i++){
            scanf("%d%d%d",&a[i].a,&a[i].b,&a[i].c);
        }
        sort(a,a+n-1,cmp);
        int ans=0;
        for(int i=0;i<n-1;i++){
            int x=ffind(a[i].a);
            int y=ffind(a[i].b);
            if(x==y)continue;
            ans+=(num[x]*num[y]-1)*(a[i].c+1);
            pre[x]=y;
            num[y]+=num[x];
        }
        printf("%d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(图论,最小生成树)