檀黎斗社长在开发神极限卡带的过程中出现了问题,他发现了图论意义上一棵有N个节点的树,但他觉得还不够好,打算动用神之才能增加若干条边,使得N个节点中任意两点都有直接相连的边,并满足新图的唯一极小连通子图仍然一开始的树。
社长知道这样有很多种增加策略,但为了公司经营,需要消耗尽量少,即新增的边权值和的最小值。
Input
第一行包含整数t,表示共有t组测试数据。
对于每组测试数据,第一行包含整数N。
接下来N-1行,每行三个整数X,Y,Z,表示X节点与Y节点之间存在一条边,长度为Z。
Output
每组数据输出一个整数,表示权值总和最小值。
每个结果占一行。
数据范围
1≤N≤6000
1≤Z≤100
题意:给你一个N个节点的树,增加若干条边形成完全图(每对不同的顶点之间都恰连有一条边相连),并满足形成的完全图所构成的最小生成树还是一开始给你的N各节点的树。
克鲁斯卡尔算法加边,普利姆算法加点。
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int N=1e6+10;
int a[N],b[N];
int sum=0;
struct node
{
int u,v,l;
}q[N];
bool cmp(node z,node x)
{
return z.l<x.l;
}
int find (int x)
{
return a[x]==x?x:find(a[x]);
}
void add(int x,int y,int z)
{
int t1=find(x);
int t2=find(y);
if(t1!=t2)
{
sum+=(z+1)*(b[t1]*b[t2]-1);
a[t2]=t1;
b[t1]+=b[t2];
}
}
int main ()
{
int t;
cin>>t;
while(t--)
{
int n;
sum=0;
cin>>n;
for(int i=1;i<=n;i++)
{
a[i]=i;
b[i]=1;
}
for(int i=1;i<n;i++)
cin>>q[i].u>>q[i].v>>q[i].l;
sort(q+1,q+n,cmp);
for(int i=1;i<n;i++)
add(q[i].u,q[i].v,q[i].l);
cout<<sum<<endl;
}
}
上图是建树时的状态转移
b数组记录节点连接的节点个数,新加入的边长加一再乘以当前两个节点连接的节点和以保证最小树是最开始的树。