「上帝一声不响,一切皆由我定」|

BadBadBad__AK

园龄:1年6个月粉丝:13关注:6

P8207 [THUPC2022 初赛] 最小公倍树 题解

题目大意

有编号为 [L,R] 区间的点,连接两个点 x,y 边权的为 LCM(x,y),求这张图的最小生成树。

1LR106,RL105

解题思路

我们有一个结论: 对于张图 G 中的一个生成子图 EE 之中的一条边 k 如果不在 E 最小生成树中,那么 k 肯定也不在 G 的最小生成树中。

那么我们考虑找一些可能是答案的边出来跑最小生成树。

对于一个 i 我们提取出所有它倍数的点,对于点 ik 来说它肯定不会去连接某个 ik 如果存在另一个更小的 ik 的话,因为这条边显然不在这张图的最小生成树中。

所以我们可以对于一个点 x 的每个约数 d,我们只连接一个最小的 d×k,然后把这些边拿出来跑 Kruskal 就好了。

时间复杂度:O(nlog2n)

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
struct node{
	ll x,y,w;
}e[11000000];
ll L,R,ans,m,fa[1100000];
bool cmp(node x,node y)
{return x.w<y.w;}
ll find(ll x)
{return (fa[x]==x)?x:(fa[x]=find(fa[x]));}
signed main()
{
	scanf("%lld%lld",&L,&R);
	for(ll i=1;i<=R;i++){
		for(ll j=i;j<=R;j+=i){
			if(j>=L){
				ll p=(L+i-1)/i*i;
				if(p==j)continue;
				e[++m]=(node){j,p,j*p/i};
			}
		}
	}
	sort(e+1,e+1+m,cmp);
	for(ll i=L;i<=R;i++)fa[i]=i;
	for(ll i=1;i<=m;i++){
		ll x=find(e[i].x),y=find(e[i].y);
		if(x==y)continue;
		ans+=e[i].w;fa[x]=y;
	}
	printf("%lld\n",ans);
	return 0;
}

本文作者:BadBadBad__AK

本文链接:https://www.cnblogs.com/BadBadBad/p/18149568/P8207

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   BadBadBad__AK  阅读(29)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起