AcWing 346 Kruskal

题意

传送门 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;
}

猜你喜欢

转载自blog.csdn.net/neweryyy/article/details/114359085