AcWing 346. 走廊泼水节

题目思路
将一个最小生成树的图,添加一些边,使得成为一个完全图,并且生成的完全图的最小生成树还是原树
算法分析
构建最小生成树的Kruskal算法

  1. 首先将所有的边按照从小到大的顺序排序
  2. 对于一条边(x,y,w),如果x和y在不在一个连通块中,就说明他们之间没有边相连那么我们相连之后,现在这两个点,各自所在的连通块(集合),都拥有了一个最短边,也就是(x,y,w)

最小生成树已经确定了,那么原来两个连通块的其他点怎么办?
假设(S_x)表示为(x)之前所在的连通块,那么(S_y)表示(y)之前所在的连通块。
由于我们不能破坏最小生成树,所以我们原来这两个连通块必须具有如下性质:假设点A属于(S_x)这个集合,点B属于(S_y)这个集合,那么A与B之间的距离,必须大于之前的(w)否者就会破坏之前的最小生成树,所以说

[A与B之间的距离最小为w+1 ]


假如(S_x)(p)个元素,然后(S_y)(q)个元素,那么将(S_x)(S_y)连通块中所有的点相连,就会增加:

[p × q - 1条边 ]

每条边的最小长度为(w + 1)
所以就会得出

[(w + 1) × (p × q)为两个连通块成为完全图的最小代价 ]

实现代码

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 6010;

struct Edge
{
    int a, b, w;
    bool operator < (const Edge & t)
    {
        return w < t.w;
    }
}e[N];

int n;
int T;
int p[N], s[N];

int find(int x)
{
    if(x != p[x]) p[x] = find(p[x]);
    return p[x];
}
int main()
{
    cin >> T;
    while(T --)
    {
        cin >> n;
        for(int i = 0;i < n - 1;i++)
        {
            cin >> e[i].a >> e[i].b >> e[i].w;
        }
        
        sort(e, e + n - 1);
        
        for(int i = 1;i <= n;i++) p[i] = i, s[i] = 1;
        int res = 0;
        for(int i = 0;i < n - 1;i++)
        {
            int a = find(e[i].a), b = find(e[i].b), w = e[i].w;
            if(a != b)
            {
                p[a] = b;
                res += (s[b] * s[a] - 1) * (w + 1);
                s[b] += s[a];
            }
        }
        cout << res << endl;
    }
    return 0;
}
原文地址:https://www.cnblogs.com/zykBlog/p/13849838.html