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

题目大意

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

\[1\leq L\leq R\leq 10^6,R-L\leq 10^5 \]

解题思路

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

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

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

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

时间复杂度:\(O(n\log^2 n)\)

代码

#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;
}

posted @ 2024-04-21 21:47  BadBadBad__AK  阅读(19)  评论(0编辑  收藏  举报