当前位置: 首页 > news >正文

最大公约数(GCD)和最小公倍数(LCM)专题练习 ——基于罗勇军老师的《蓝桥杯算法入门C/C++》

一、1.互质数的个数 - 蓝桥云课

算法代码(只能通过30%):

#include <bits/stdc++.h>  // 包含所有标准库头文件,方便编程
using namespace std;      // 使用标准命名空间,避免每次调用标准库函数时写 std::
typedef long long ll;     // 定义 long long 类型的别名 ll,方便使用

ll mod = 998244353;       // 定义一个常量 mod,值为 998244353,用于模运算

// 计算最大公约数(GCD)的函数
ll gcd(ll a, ll b) {
    return b ? gcd(b, a % b) : a;  // 递归实现欧几里得算法,b 为 0 时返回 a,否则递归计算 gcd(b, a % b)
}

// 快速幂函数,计算 a 的 n 次方模 mod
ll fastPow(ll a, ll n) {
    ll ans = 1;           // 初始化结果为 1
    a %= mod;             // 对 a 取模,避免溢出
    while (n) {           // 当 n 不为 0 时循环
        if (n & 1) {      // 如果 n 的最低位是 1
            ans = (ans * a) % mod;  // 将 a 乘到结果中,并对 mod 取模
        }
        a = (a * a) % mod; // 将 a 平方,并对 mod 取模
        n >>= 1;           // 将 n 右移一位,相当于 n /= 2
    }
    return ans;           // 返回最终结果
}

int main() {
    ll a, b;              // 定义两个长整型变量 a 和 b
    cin >> a >> b;        // 从标准输入读取 a 和 b 的值

    ll mi = fastPow(a, b); // 计算 a 的 b 次方模 mod,结果存储在 mi 中

    ll ans = 0;           // 初始化计数器 ans 为 0
    for (int i = 1; i < mi; i++) {  // 遍历从 1 到 mi-1 的所有整数
        if (gcd(i, mi) == 1) {     // 如果当前整数 i 与 mi 的最大公约数为 1(即互质)
            ans++;                 // 计数器 ans 加 1
        }
    }
    cout << ans;          // 输出最终的计数结果
    return 0;             // 程序正常结束
}

 算法代码(通过100%):

// 本题主要考察欧拉函数和快速幂
// 欧拉函数 Euler(n): 表示不大于 n 且与 n 互质的正整数的个数,Euler(1) = 1
// 由唯一分解定理,n = p1^k1 * p2^k2 * ... * pn^km,pi 均为质数,ki 是其幂次
// 由此可推出欧拉函数的求法:Euler(n) = n / p1 * (p1 - 1) / p2 * (p2 - 1) / ... / pn * (pn - 1)
// 将欧拉函数的模板背下来即可
// 由欧拉函数的模板可知,若已知 Euler(a) = m,则 Euler(a^b) = m * (a^(b-1))
// 故先求 Euler(a),再用快速幂求 a^(b-1),二者相乘即为最终答案

#include <bits/stdc++.h>  // 包含所有标准库头文件,方便编程
using namespace std;      // 使用标准命名空间,避免每次调用标准库函数时写 std::

const int mod = 998244353; // 定义一个常量 mod,值为 998244353,用于模运算

typedef unsigned long long ull; // 定义 unsigned long long 类型的别名 ull,方便使用

// 快速幂算法,计算 base^power % mod
ull quick_power(ull base, ull power, ull mod) {
    ull res = 1; // 初始化结果为 1
    while (power) { // 当 power 不为 0 时循环
        if (power & 1) // 如果 power 的最低位是 1
            res = res * base % mod; // 将 base 乘到结果中,并对 mod 取模
        base = base * base % mod; // 将 base 平方,并对 mod 取模
        power = power >> 1; // 将 power 右移一位,相当于 power /= 2
    }
    return res % mod; // 返回最终结果
}

// 求 n 的欧拉函数(固定模板)
ull Euler(ull n) {
    ull phi = n; // 初始化 phi 为 n
    for (int i = 2; i * i <= n; i++) { // 枚举 n 的质因数
        if (n % i) continue; // 如果 i 不是 n 的因数,跳过
        while (n % i == 0) { // i 是质因数
            n = n / i; // n 不断除以 i 直至 i 不再是 n 的质因数
        }
        phi = phi / i * (i - 1); // 递推欧拉函数,Euler(n) = n / pi * (pi - 1)
    }
    // 最后可能还剩下一个大于 sqrt(n) 的质因数,如 12 = 2 * 2 * 3,最后将剩下 3,补充上
    if (n > 1) phi = phi / n * (n - 1);
    return phi; // 返回欧拉函数值
}

// 由如上算法可知,n 的欧拉函数只与其质因数的组成有关,与每个质因数的个数无关
// 对于不同的数字,只要它们的质因数组成相同,计算过程中就会除以相同的 pi 乘以相同的 (pi - 1)
// 故若 m 和 n 的质因数组成相同,m 是 n 的 k 倍,则 Euler(m) 也是 Euler(n) 的 k 倍
// 而 a^b 是 a 的 a^(b-1) 倍,则由此推出 Euler(a^b) = Euler(a) * (a^(b-1)),此即为最终答案

int main() {
    ull a, b; // 定义两个无符号长整型变量 a 和 b
    cin >> a >> b; // 从标准输入读取 a 和 b 的值

    ull Euler_a = Euler(a); // 计算 a 的欧拉函数值
    // 最终答案:Euler(a) * a^(b-1) % mod
    ull ans = Euler_a * quick_power(a, b - 1, mod) % mod;
    cout << ans << endl; // 输出最终答案
    return 0; // 程序正常结束
}

二、1.等差数列 - 蓝桥云课

算法代码:

#include <bits/stdc++.h>  // 包含所有标准库头文件
using namespace std;      // 使用标准命名空间

int a[100000];  // 定义一个全局数组 a,用于存储输入的整数,最大容量为 100000

int main() {
    int n;  // 定义一个变量 n,用于存储输入的整数个数
    cin >> n;  // 从标准输入读取整数个数 n

    // 循环读取 n 个整数并存储到数组 a 中
    for (int i = 0; i < n; i++) {
        cin >> a[i];  // 读取第 i 个整数并存储到数组 a 中
    }

    sort(a, a + n);  // 对数组 a 中的元素进行升序排序

    int d = 0;  // 定义一个变量 d,用于存储数组中相邻元素的差值的最大公约数(GCD)

    // 计算数组中相邻元素的差值的最大公约数
    for (int i = 1; i < n; i++) {
        d = __gcd(d, a[i] - a[i - 1]);  // 更新 d 为当前 d 和相邻元素差值的 GCD
    }

    // 判断 d 是否为 0
    if (d == 0) {
        cout << n << endl;  // 如果 d 为 0,说明所有元素相同,输出 n
    } else {
        // 如果 d 不为 0,计算等差数列的项数并输出
        printf("%d\n", (a[n - 1] - a[0]) / d + 1);
    }

    return 0;  // 程序正常结束
}

主要思路:

三、1.核桃的数量 - 蓝桥云课

算法代码: 

#include <bits/stdc++.h>  // 包含所有标准库头文件
using namespace std;      // 使用标准命名空间

// 定义一个函数 lcm,用于计算两个数的最小公倍数
int lcm(int a, int b) {
    return a / __gcd(a, b) * b;  // 使用公式 LCM(a, b) = (a * b) / GCD(a, b)
}

int main() {
    int a, b, c;  // 定义三个整数变量 a, b, c
    cin >> a >> b >> c;  // 从标准输入读取三个整数 a, b, c

    int k = lcm(a, b);  // 计算 a 和 b 的最小公倍数,并将结果存储在变量 k 中
    cout << lcm(k, c) << endl;  // 计算 k 和 c 的最小公倍数,并输出结果

    return 0;  // 程序正常结束
}

四、P1820 - [NewOJ Week 6] 最小公倍数 - New Online Judge 

算法代码:

#include <bits/stdc++.h>  // 包含所有标准库头文件
using namespace std;      // 使用标准命名空间
typedef long long ll;     // 定义 long long 类型为 ll,方便使用
const ll INF = 1e18;      // 定义 INF 为 10^18,表示最大值
map<ll, pair<int, int>> ans;  // 定义一个 map,存储 n 对应的区间 [L, R]

// 预处理函数,计算所有可能的 n 对应的 [L, R]
void init() {
    // 遍历 L 从 1 到 2e6
    for (ll L = 1; L <= 2000000; L++) {
        ll n = L * (L + 1);  // 计算 L 和 L+1 的乘积
        // 遍历 R 从 L+2 开始,逐步增加
        for (ll R = L + 2; ; R++) {
            ll g = __gcd(n, R);  // 计算 n 和 R 的最大公约数
            // 检查是否溢出,如果溢出则跳出循环
            if (n / g > INF / R) break;
            n = n / g * R;  // 更新 n 的值,防止溢出
            // 如果 n 还没有被存储过,则存入 map 中
            if (!ans.count(n)) {
                ans[n] = make_pair(L, R);
            }
        }
    }
}

int main() 
{
    init();  // 调用预处理函数
    int T; scanf("%d", &T);  // 读取测试数据组数 T
    while (T--) 
    {
        ll n; scanf("%lld", &n);  // 读取每组测试数据的 n
        // 先判断区间长度为 2 的情况: [L, L+1]
        ll sqrt_n = sqrt(n + 0.5);  // 计算 n 的平方根
        pair<int, int> res;  // 定义结果变量 res
        // 检查是否存在区间 [sqrt_n, sqrt_n + 1] 满足条件
        if (sqrt_n * (sqrt_n + 1) == n) 
        {
            res = make_pair(sqrt_n, sqrt_n + 1);  // 如果满足条件,存储结果
            // 如果 map 中已经有更优的解,则更新 res
            if (ans.count(n))
                if(res.first > ans[n].first) 
                    res = ans[n];
        }
        else if (ans.count(n)) 
        {  
            // 如果 map 中有对应的解,则直接使用
            res = ans[n];
        } 
        else 
        {  
            // 如果没有解,则输出 -1 并继续下一组测试
            puts("-1");
            continue;
        }
        printf("%d %d\n", res.first, res.second);  // 输出结果
    }
    return 0;
}

 代码思路

1. 预处理阶段

  • 目标:预先计算所有可能的 n 值及其对应的区间 [L,R],并将结果存储在 map 中。

  • 实现

    • 遍历 L 从 1 到 2×10^6。

    • 对于每个 L,计算 L 和 L+1 的乘积 n。

    • 遍历 R 从 L+2开始,逐步增加,计算区间 [L,R] 内所有整数的乘积 n。

    • 使用最大公约数(__gcd)防止溢出,并更新 n 的值。

    • 如果 n 没有被存储过,则将其存入 map 中。

2. 查询阶段

  • 目标:对于每个输入的 n,找到对应的区间 [L,R] 或输出 -1 表示无解。

  • 实现

    • 调用预处理函数 init(),生成所有可能的 n 和 [L,R]的映射。

    • 读取测试数据组数 T。

    • 对于每组测试数据:

      • 读取 n。

      • 检查是否存在长度为 2 的区间 [L,L+1] 满足 L×(L+1)=n。

      • 如果存在,则检查 map 中是否有更优的解(即 L 更小的解)。

      • 如果 map 中有对应的解,则直接使用。

      • 如果无解,则输出 -1

      • 输出结果 [L,R]。


代码思路总结

  1. 预处理

    • 遍历 L 从 1 到 2×10^6。

    • 对于每个 L,计算区间 [L,R] 的乘积 n,并存储到 map 中。

    • 使用最大公约数防止溢出,确保计算正确。

  2. 查询

    • 对于每个输入的 n,先检查是否存在长度为 2 的区间 [L,L+1] 满足条件。

    • 如果存在,则检查 map 中是否有更优的解。

    • 如果 map 中有解,则直接使用;否则输出 -1

  3. 输出

    • 输出每组测试数据的结果 [L,R] 或 -1


关键点

  • 预处理优化:通过限制 L的范围(1 到 2×10^6),减少计算量。

  • 防止溢出:使用最大公约数(__gcd)和先除后乘的方式,避免乘积溢出。

  • 区间长度为 2 的特殊处理:单独检查 [L,L+1]的情况,减少计算复杂度。

  • 结果存储:使用 map 存储 n 和 [L,R]的映射,方便快速查询。


代码结构

  1. 预处理函数 init()

    • 遍历 L 和 R,计算 n 并存储到 map 中。

  2. 主函数 main()

    • 调用 init() 进行预处理。

    • 读取测试数据组数 T。

    • 对于每组测试数据:

      • 读取 n。

      • 检查是否存在长度为 2 的区间 [L,L+1]。

      • 检查 map 中是否有解。

      • 输出结果。

http://www.dtcms.com/a/80337.html

相关文章:

  • 蓝桥杯2023年第十四届省赛真题-接龙数列
  • Linux后门程序工作原理的详细解释,以及相应的防御措施
  • c语言数据结构 双循环链表设计(完整代码)
  • Ubuntu版免翻墙搭建BatteryHistorian
  • freeswitch(开启抓包信息)
  • 观察RenderDoc截帧UE时“Event”代表什么
  • ssh 多重验证的好处:降低密钥长度,动态密码
  • 分布式任务调度
  • 事件响应计划:网络弹性的关键
  • C++ :try 语句块和异常处理
  • IDEA批量替换项目下所有文件中的特定内容
  • Python Cookbook-4.7 在行列表中完成对列的删除和排序
  • 主流加固方案深度剖析(梆梆/腾讯/阿里)
  • 《数据库原理教程》—— 第三章 关系数据模型 笔记
  • 解释 RESTful API,以及如何使用它构建 web 应用程序
  • Linux驱动开发实战(七):pinctrl引脚管理入门结合数据手册分析
  • Powershell WSL导出导入ubuntu22.04.5子系统
  • 1.5.5 掌握Scala内建控制结构 - 异常处理
  • 编写脚本在Linux下启动、停止SpringBoot工程
  • 强大的AI网站推荐(第一集)—— Devv AI
  • 串口部分问题
  • 21、web前端开发之html5(二)
  • Deepseek+飞书实现简历分析建议+面试题
  • Android 13深度定制:SystemUI状态栏时间居中显示终极实战指南
  • Day37 | 739. 每日温度、496. 下一个更大元素 I、503. 下一个更大元素 II、42. 接雨水、84. 柱状图中最大的矩形
  • PH热榜 | 2025-03-19
  • Windows10抓包工具Wireshark下载、安装、使用
  • Java基础面试题学习
  • selenium(鼠标操作、页面操作、用例设计)
  • BYU-YOLO数据格式准备