NOIP 模拟赛 day11 T3 最小边权和 水题解

Description

给你一棵带边权的树,这棵树是某个完全图唯一的最小生成树。问原来的完全图中所有边可能的最小边权和是多少。完全图是任意两个点之间都有边相连的图。

\(\small T\leq 10\ \ \ N\leq 2\cdot 10^4\ \ \ w_i \leq 10^4\)

Solution

题目要求的是完全图的最小边权和,并且题目给了我们最小生成树,我们就要想怎么加,什么时候加其他的那些非树边。

如果我们想尽量早的加入尽可能小的非树边,我们就可以考虑,就按照最小生成树的加边进行遍历。

然后我们考虑,什么时候加哪些边。

一个很简单的例子:

image

这是一个完全图,照理来说,最小生成树应该是 \((1,2),(1,3)\) ,然后多出来的 \((2,3)\) 就能使最小生成树变成完全图。

所以说,当我们加边加到一定程度( \(N>=2\) ),完全图和最小生成树边数就不一样了,然后在新加了一条边的时候,我们就可以处理两者之间差的边,为了不对原树产生影响,这些边全部赋成目前加的树边的边权+1就行了。

注意新加的树边可能意味着两大块的合并,所以多出来的边的数量与所在集合大小有关,所以做模拟最小生成树的时候记录一下集合大小。

时间复杂度 \(O(NlogN)\) (代码清奇,绝对好懂)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e4+10;
ll t,n,fa[N],siz[N],ans;
struct mdzz{
	ll u,v,w;
	bool operator < (const mdzz& it) const{
		return w<it.w;
	}
}edge[N];
inline ll read(){
	ll s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
	return s*w;
}
inline ll find(ll x){
	if(fa[x]==x)return fa[x];
	return fa[x]=find(fa[x]);
}
int main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	t=read();
	while(t--){
		n=read();ans=0;
		for(int i=1;i<=n;++i){
			fa[i]=i;siz[i]=1;
		}
		for(int i=1;i<n;++i){
			int u=read(),v=read(),w=read();
			edge[i]=(mdzz){u,v,w};ans+=w;
		}
		sort(edge+1,edge+n);
		for(int i=1;i<n;++i){
			int uu=find(edge[i].u);
			int vv=find(edge[i].v);
			ans+=(siz[uu]*siz[vv]-1ll)*(edge[i].w+1ll);
			fa[vv]=uu;siz[uu]+=siz[vv];siz[vv]=0;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

刮大分!

posted @ 2021-07-22 18:05  Illusory_dimes  阅读(57)  评论(0编辑  收藏  举报