构造完全图

走廊泼水节(构造完全图)ACWing346

题意:

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

思路:

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

code

#include <bits/stdc++.h>
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 >> t ;
    while( t-- )
    {
        memset(head , 0 , sizeof(head)) ;
        memset(ed , 0 , sizeof(ed)) ;
        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) ;
        }
        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 ; 
}
posted @   Simon_...sun  阅读(89)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示