做法:初始时先将每一个点看成一个大小为$1$的连通块,这个连通块就可以看成一个完全图(因为只有一个点)
做Kruskal
算法,在每循环到一条可以合并两个连通块的边$e$时,记$e$的边长为$w$,为了形成一个完全图,就要使得两个已经是完全图的连通块中的点有边,但是为了使最后的唯一最小生成树还是原来那棵而且,新增的边一定要大于$w$:
-
假设新边小于w,因为新增边后会成环,当断开边e,形成的树大小会变小,即不是原来那棵,所以不成立
-
假设新边等于w,同样的断开e,会形成一个大小一样但结构不一样的树,不满足唯一,所以也不成立。
所以只要在每次新增e的时候,给两个连通块内的点增加w+1长的边即可。
- 证明:得出来的完全图中的最小生成树是原来那棵。(反证法)
假设最后生成的完全同中的最小生成树不是原来那棵,在原树中,从小到大遍历n-1条边,找出第一条不在新最小生成树中的边,在新树中将它连上,会形成一个环,由之前加边时的操作可以知道,在这个环中一定存在一条长度大于它的边,断开这更大条,会形成一个更小的树,那就不是最小生成树,所以假设不成立。
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 6010;
struct Edge{
int u , v , w;
bool operator < (const Edge &W) const{
return w < W.w;
}
}edge[N];
int n;
int f[N];
int cnt[N];
int find(int x)
{
return f[x] = (f[x] == x ? x : find(f[x]));
}
int main()
{
int T;
cin >> T;
while(T--)
{
cin >> n;
for(int i = 1 ; i <= n ; i++) f[i] = i , cnt[i] = 1;
for(int i = 0 ; i < n - 1; i++)
{
int u , v , w;
cin >> u >> v >> w;
edge[i] = {u , v , w};
}
sort(edge , edge + n - 1);
int res = 0;
for(int i = 0 ; i < n - 1 ; i ++)
{
auto e = edge[i];
int u = find(e.u) , v = find(e.v) , w = e.w;
if(u != v)
{
res += (cnt[u] * cnt[v] - 1) * (w + 1);
f[u] = v;
cnt[v] += cnt[u];
}
}
cout << res << endl;
}
return 0;
}
是通过统计两个连通块中分别有多少个点吗?然后-1是因为之前已经存在一条长为w的边
这道题不知道题目和题有什么关系。。读半天没读懂
是的
就是给定一棵N个点的树,然后要给这个图加边,使得每个点之间都有边,且这幅完全图的最小生成树还是原来那棵
算新加的边,不算原来的
题目背景被去掉了呗
res += (cnt[u] * cnt[v] - 1) * (w + 1);
这里的w + 1
指的是加的长度为w + 1
吗是的
n个点的完全图是唯一的 咋可能让完全图的唯一最小生成树仍然是这棵树呢。
增加的边的边权是不一定的,所以我们可以通过设置添加的边的边权,来使得最后的完全图的最小生成树是原树,这也是这道题目让我们求的(求添加的总边权最小是多少)。
哇 一语惊醒梦中人 谢谢大佬
加油hh~
终于明白了,就是边权让我们自己赋值啊
是的