题意
传送门 AcWing 346 走廊泼水节
题解
考虑 K r u s k a l Kruskal Kruskal 构造最小生成树的过程,即总是维护最小生成森林。依次考虑向合并的连通分量间加边,当最小生成树构造完成,加的边与树边正好构成完全图。具体而言,设合并的连通分量为 x , y x,y x,y,节点数为 s z [ x ] , s z [ y ] sz[x],sz[y] sz[x],sz[y],连接的边权值为 z z z,则合并时需要增加边 s z [ x ] × s z [ y ] − 1 sz[x]\times sz[y]-1 sz[x]×sz[y]−1 条,权值最小且保证最小生成树唯一,则所添加的边权值要大于 z z z,则合并连通分量对答案的贡献为 ( s z [ x ] × s z [ y ] − 1 ) × ( z + 1 ) (sz[x]\times sz[y]-1)\times (z+1) (sz[x]×sz[y]−1)×(z+1)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 6000;
struct edge
{
int x, y, z;
bool operator<(const edge &b) const {
return z < b.z; }
} es[maxn];
int T, N, fa[maxn], sz[maxn];
ll res;
int find(int x) {
return fa[x] == x ? x : (fa[x] = find(fa[x])); }
void merge(int i)
{
int x = find(es[i].x), y = find(es[i].y);
if (sz[x] < sz[y])
swap(x, y);
res += (ll)(sz[x] * sz[y] - 1) * (es[i].z + 1);
fa[y] = x, sz[x] += sz[y];
}
int main()
{
scanf("%d", &T);
while (T--)
{
scanf("%d", &N);
for (int i = 1; i < N; ++i)
scanf("%d%d%d", &es[i].x, &es[i].y, &es[i].z);
sort(es + 1, es + N);
for (int i = 1; i <= N; ++i)
fa[i] = i, sz[i] = 1;
res = 0;
for (int i = 1; i < N; ++i)
merge(i);
printf("%lld\n", res);
}
return 0;
}