[THUPC2022 初赛] 最小公倍树(最小生成树,Kruskal优化建图)

文章讲述了如何在给定范围内构建最小公倍数树并计算其边权和,涉及快速求公约数和Kruskal算法的应用。
摘要由CSDN通过智能技术生成

[THUPC2022 初赛] 最小公倍树

题目背景

听说有人嫌题面描述都太长了。

题目描述

对于任意 V ⊂ N ∗ V\subset\mathbb{N}^* VN ∣ 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 1LR106,且 R − L ≤ 1 0 5 R-L\le 10^5 RL105

思路

这里求边权之和说的就是生成树了,但如果我们单纯的去建树,那么可能会超时,因为有的边不需要建树。而且题目要求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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

green qwq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值