[THUPC2022 初赛] 最小公倍树
题目背景
听说有人嫌题面描述都太长了。
题目描述
对于任意 V ⊂ N ∗ V\subset\mathbb{N}^* V⊂N∗, ∣ V ∣ < + ∞ |V|<+\infty ∣V∣<+∞,构造一张无向完全图 G = ( V , E ) G=(V,E) G=(V,E),其中 ( u , v ) (u, v) (u,v) 的边权为 u , v u,v u,v 的最小公倍数 l c m ( u , v ) \mathrm{lcm}(u, v) lcm(u,v)。称 G G G 的最小生成树为 V V V 的最小公倍树(LCT, Lowest Common Tree)。
现在给出 L , R L, R L,R,请你求出 V = L , L + 1 , ⋯ , R V={L, L+1, \cdots, R} V=L,L+1,⋯,R 的最小公倍树 L C T ( V ) LCT(V) LCT(V)。
输入格式
输入仅一行,包括两个正整数 L , R L, R L,R。
输出格式
输出一个正整数,表示 L C T ( V ) LCT(V) LCT(V) 的边权和。
样例 #1
样例输入 #1
3 12
样例输出 #1
126
样例 #2
样例输入 #2
6022 14076
样例输出 #2
66140507445
样例 #3
样例输入 #3
13063 77883
样例输出 #3
3692727018161
样例 #4
样例输入 #4
325735 425533
样例输出 #4
1483175252352926
提示
【样例解释】
其中一种最小公倍树上的边为 ( 3 , 4 ) , ( 3 , 5 ) , ( 3 , 6 ) , ( 3 , 7 ) , ( 4 , 8 ) , ( 3 , 9 ) , ( 5 , 10 ) , ( 3 , 11 ) , ( 3 , 12 ) (3, 4), (3, 5), (3, 6), (3, 7), (4, 8), (3, 9), (5, 10), (3, 11), (3, 12) (3,4),(3,5),(3,6),(3,7),(4,8),(3,9),(5,10),(3,11),(3,12)。
【数据范围】
对于 100 100% 100 的数据,保证 1 ≤ L ≤ R ≤ 1 0 6 1\le L\le R\le 10^6 1≤L≤R≤106,且 R − L ≤ 1 0 5 R-L\le 10^5 R−L≤105。
思路
这里求边权之和说的就是生成树了,但如果我们单纯的去建树,那么可能会超时,因为有的边不需要建树。而且题目要求lcm(a,b),那么我们就得装化成a*b/gcd(a,b),所以要求lcm最小,那么我们就要使得gcd(a,b)最大,因此什么时候gcd会比较大呢,那肯定是有共同的约数。
因此这里拓展一下快速求在一个区间内的共同约数的两个数:
for(int i=1;i<=r;i++){//枚举因子
for(int j=i;j<=r;j+=i){//枚举能被它整除的(也就是因子乘以数)
if(j>=l){
int p=(l+i-1)/i*i;//用来求以i为因数的数
然后这两个数的lcm为:j*p/i
}
}
}
总的来说参考:
代码
//kruskal
#include<iostream>
#include<algorithm>
#include<cstring>
#define int long long
using namespace std;
const int N = 2e6+10;
struct E{
int x,y,w;
}e[N];
int l,r,cnt;
int p[N];
int find(int x){
if(x!=p[x])p[x]=find(p[x]);
return p[x];
}
signed main(){
cin>>l>>r;
//常用写法,类筛质数,为什么呢?因为v*w/gcd(v,w)最小,那么gcd(v,w)要最大。
//什么时候最大呢?肯定是二者存在约数
for(int i=1;i<=r;i++){//枚举因子
for(int j=i;j<=r;j+=i){//枚举能被它整除的(也就是因子乘以数)
if(j>=l){
int p=(l+i-1)/i*i;//用来求以i为因数的数
if(j==p)continue;
e[++cnt]={j,p,j*p/i};
}
}
}
sort(e+1,e+1+cnt,[&](E a,E b){
return a.w<b.w;
});
for(int i=l;i<=r;i++)p[i]=i;
int res=0;
for(int i=1;i<=cnt;i++){
int a=e[i].x,b=e[i].y,w=e[i].w;
int pa=find(a),pb=find(b);
if(pa!=pb){
p[pa]=pb;
res+=w;
}
}
cout<<res;
return 0;
}