走廊泼水节
题意
给定一棵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<iostream>
#include<cstdio>
#include<algorithm>
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;
}