构造完全图

题意:

给你一棵树,将这棵树扩充为完全图,满足该图的最小生成树为这棵树。求边权和值最小是多少。

思路:

根据完全图的性质,每两个点都至少有一条边联通。再想 KruskalKruskal 算法的实现流程,我们只需要在合并两个单独并查集的时候,记录一下贡献就可以了。因为在 KruskalKruskal 合并两个并查集的时候只是保留了一条边权最小的边使两个单独的联通块 ZZYY 联通。所以如要构成完全图,根据乘法原理,每次操作的贡献就为 SZSY1\large S_Z * S_Y - 1 S\large S 为联通块中节点的个数,减去的是已经被加入 MSTMST 中的树边。由于要满足扩充后的完全图的 MSTMST 仍然为这棵树, 那么加入的非树边的权值需要严格大于树边(如果小于就会被加入 MSTMST 中使原树结构被改变),同时又要满足 增加边的权值最小 所以这里每次合并的贡献就为 (SZSY1)(w+1)\large (S_Z * S_Y - 1 ) * (w + 1)ww 为树边的权值。

code

#include <bits/stdc++.h>
#define int long long 
const int maxn = 2e5 + 7 ;
using namespace std ;
struct node 
{
    int frm , to , nxt , dis ;
} ed[maxn] ;
bool operator < (node x , node y)
{
	return x.dis < y.dis ;
}
int n , m , cnt , tot , ans , t  ;
int head[maxn] , fa[maxn] , s[maxn] ;
int find(int x)
{
	if(x == fa[x])	return x ;
	return fa[x] = find(fa[x]) ;
} 
void add(int u , int v , int w)
{
    ed[++cnt] = { u , v , head[u] , w } ;
    head[u] = cnt ;
}
signed main()
{
        cin >> n ;  cnt = 0 , tot = 0 , ans = 0 ;   
 		for(int i = 0 ; i <= 2*n ; i++ )	fa[i] = i , s[i] = 1 ; 
        for(int i = 1 ; i < n ; i++ )
        {
            int u , v , w ;     cin >> u >> v >> w ; 
            add(u , v , w) ;    add(v , u , w) ;
            ans += w ;
        }
        sort(ed + 1 , ed + 2*n + 1) ; 
        for(int i = 1 ; i <= 2*n ; i++ )
        {
            int u = find(ed[i].frm) , v = find(ed[i].to) ;
            if(u == v)  continue ;
            ans += (ed[i].dis + 1) * (s[u] * s[v] - 1) ;
            fa[v] = u ; s[u] += s[v] ;  tot++ ;
        //cout << "suz_ak_ioi" << s[v]  ;
            if(tot == n)    break ;
        }
        cout << ans << '\n' ;
    return 0 ; 
}