一道另类生成树
原题链接
将输入的树的(n-1)条边按从小到大排序,然后(Kruskal)在生成该树的过程中计算新增边的总长。
当在连第(i)条边,设该边的两端点为(x,y),长度为(z),分别属于(S_x)和(S_y)两个并查集中。
而为了将树变成完全图,显然要将(S_x,S_y)两个并查集中所有点一一连边,所以除去原有的这条边,剩下共(|S_x| imes|S_y|-1)条边需要被添加。
而为了使完全图的最小生成树是原有树,所以在添加这(|S_x| imes|S_y|-1)条边时,必须使得这些边比原有边要长,同时由于要使得添加的边长和最小,所以这些边的长度均为(z+1)。
所以总长为((z+1) imes(|S_x| imes|S_y|-1)),将其累加至答案即可。
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 6010;
struct dd {
int x, y, z;
};
dd a[N];
int fa[N], S[N];
int re()
{
int x = 0;
char c = getchar();
bool p = 0;
for (; c<'0' || c>'9'; c = getchar())
p |= c == '-';
for (; c >= '0'&&c <= '9'; c = getchar())
x = x * 10 + (c - '0');
return p ? -x : x;
}
int fin(int x)
{
if (fa[x] == x)
return x;
return fa[x] = fin(fa[x]);
}
int comp(dd x, dd y)
{
return x.z < y.z;
}
int main()
{
int i, x, y, n, t;
long long s;
t = re();
while (t--)
{
n = re();
for (S[n] = i = 1, fa[n] = n; i < n; i++)
{
a[i].x = re();
a[i].y = re();
a[i].z = re();
fa[i] = i;
S[i] = 1;
}
sort(a + 1, a + n, comp);
for (i = 1, s = 0; i < n; i++)
{
x = fin(a[i].x);
y = fin(a[i].y);
if (x != y)
{
s += 1LL * (a[i].z + 1)*(S[x] * S[y] - 1);
fa[x] = y;
S[y] += S[x];
}
}
printf("%lld
", s);
}
return 0;
}