【题解】「一本通 3.1 练习 2」构造完全图

网上这道题的题解似乎不多?虽然这是道水题,但是……还是记一记吧。非常啰嗦,有朝一日希望能做到言简意赅。

建议配合歌单食用:Prokofiev: Romeo and Juliet

题意

对于完全图\(G\)若有且仅有一棵最小生成树为\(T\),则称完全图\(G\)是树\(T\)扩展出的。

给你一棵树\(T\),找出\(T\)能扩展出的边权和最小的完全图\(G\)

思路

大佬言:当正向思维受阻时,逆向思维有奇效。

但是这是一道正向思维题,考察对算法的理解。(题目言简意赅,看起来不像过于复杂的题)

我们可以用递推的思想,考虑当我们有一个完全图\(G_1\)时我们该怎么让这个图变大。显然,我们希望情况越简单越好,所以我们不直接连一个连通块,而往\(G_1\)里加一个点\(u\)。这时我们要做的只是给\(u\)\(G_1\)里的每一个点连上一条边。由于要满足\(T\)是最小生成树,因此,除题目中给出的\(\in T\)的、连接\(P\)\(G_1\)的边\(d\)外,其它新增的边,边权都为\(d+1\)(扩展出的边权和最小)。

当题目涉及连通块时,可以考虑用并查集这一结构。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+5;
int n;
int con[N],fa[N];//con记录连通块的规模 
struct edge{
	int s,t,d;
}e[N];
ll ans;

int findf(int x){
	con[x]=con[fa[x]];
	if(fa[x]!=x) return fa[x]=findf(fa[x]);
	return x;
}

bool cmp(edge x,edge y){
	return x.d<y.d;
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<n;i++){
		scanf("%d%d%d",&e[i].s,&e[i].t,&e[i].d);
		con[i]=1;
		fa[i]=i;
	}
	con[n]=1;
	fa[n]=n;
	sort(e+1,e+n,cmp);
	for(int i=1;i<n;i++){
		int u=findf(e[i].s),v=findf(e[i].t);
		if(u!=v){
			ans+=(ll)(con[u]*con[v]-1)*(ll)(e[i].d+1)+(ll)e[i].d;
			con[u]+=con[v];
			con[v]=con[u];
			fa[u]=fa[v];
		}	
	}
	printf("%lld",ans);
	return 0;
}

你看,压根不用记起\(Kruskal\)这个名字。

posted @ 2022-04-27 21:53  Searshkiu  阅读(154)  评论(0编辑  收藏  举报