刷题笔记-小C的数字倍数问题 | 豆包MarsCode AI刷题

157 阅读3分钟

www.marscode.cn/practice/vk…

小U对数字倍数问题很感兴趣,她想知道在区间 [l,r][l, r] 内,有多少个数是 aa 的倍数,或者是 bb 的倍数,或者是 cc 的倍数。需要计算出这些数的个数。

示例:

  • 输入:a=2,b=3,c=4,l=1,r=10a = 2, b = 3, c = 4, l = 1, r = 10
  • 输出:7

二、解题思路

1. 分析问题

这个问题要求统计在给定区间内,满足以下任一条件的整数的数量:

  • aa 的倍数
  • bb 的倍数
  • cc 的倍数

需要注意的是,一个数可能同时是多个数的倍数,例如,1212 既是 33 的倍数,也是 44 的倍数。

2. 避免重复计数

为了准确地统计符合条件的整数个数,必须避免重复计数。因此,我们可以使用 容斥原理 来解决这个问题。

3. 容斥原理

容斥原理用于计算多个集合的并集的大小,公式如下:

ABC=A+B+CABACBC+ABC|A \cup B \cup C| = |A| + |B| + |C| - |A \cap B| - |A \cap C| - |B \cap C| + |A \cap B \cap C|

其中:

  • A|A| 表示集合 AA 的元素个数
  • AB|A \cap B| 表示集合 AABB 的交集的元素个数

在本题中:

  • AA:所有是 aa 的倍数的数
  • BB:所有是 bb 的倍数的数
  • CC:所有是 cc 的倍数的数

三、算法步骤

1. 计算单独倍数的数的个数

计算在区间 [l,r][l, r] 内是 aabbcc 的倍数的数的个数。

公式:

Count(k)=rkl1k\text{Count}(k) = \left\lfloor \frac{r}{k} \right\rfloor - \left\lfloor \frac{l - 1}{k} \right\rfloor

2. 计算两两的最小公倍数(LCM)及其倍数的个数

计算 aabb 的最小公倍数 lcm(a,b)\text{lcm}(a, b),以及 lcm(a,c)\text{lcm}(a, c)lcm(b,c)\text{lcm}(b, c),然后计算这些最小公倍数在区间内的倍数的个数。

3. 计算三个数的最小公倍数及其倍数的个数

计算 aabbcc 三个数的最小公倍数 lcm(a,b,c)\text{lcm}(a, b, c),计算其在区间内的倍数的个数。

4. 应用容斥原理计算总数

将上述结果代入容斥原理公式,计算最终的整数个数。


四、算法实现

def solution(a: int, b: int, c: int, l: int, r: int) -> int:
    from math import gcd

    # 定义计算最小公倍数的函数
    def lcm(x, y):
        return x * y // gcd(x, y)

    # 定义计算在区间内是 k 的倍数的数的个数的函数
    def get_count(k, l, r):
        return r // k - (l - 1) // k

    # 计算单独倍数的数的个数
    n_a = get_count(a, l, r)
    n_b = get_count(b, l, r)
    n_c = get_count(c, l, r)

    # 计算两两的最小公倍数及其倍数的个数
    lcm_ab = lcm(a, b)
    lcm_ac = lcm(a, c)
    lcm_bc = lcm(b, c)

    n_ab = get_count(lcm_ab, l, r)
    n_ac = get_count(lcm_ac, l, r)
    n_bc = get_count(lcm_bc, l, r)

    # 计算三个数的最小公倍数及其倍数的个数
    lcm_abc = lcm(a, lcm(b, c))
    n_abc = get_count(lcm_abc, l, r)

    # 应用容斥原理计算总数
    total = n_a + n_b + n_c - n_ab - n_ac - n_bc + n_abc

    return total

# 测试样例
if __name__ == "__main__":
    print(solution(2, 3, 4, 1, 10) == 7)        # 输出:True
    print(solution(5, 7, 11, 15, 100) == 34)    # 输出:True
    print(solution(1, 1, 1, 1, 1000) == 1000)   # 输出:True

五、算法分析

1. 时间复杂度

  • 计算最大公约数(GCD)O(logmin(a,b))O(\log \min(a, b))
  • 计算最小公倍数(LCM)O(logmin(a,b))O(\log \min(a, b))
  • 总的计算次数:常数次计算,时间复杂度为 O(logmax(a,b,c))O(\log \max(a, b, c))

2. 空间复杂度

  • 仅使用了常数个变量,空间复杂度为 O(1)O(1)

六、注意事项

1. 整数除法与下取整

  • 在 Python 中,使用 // 进行整除运算,结果为向下取整的整数
  • 避免使用 /,因为其结果为浮点数

2. 容斥原理的加减顺序

  • 按照容斥原理的公式,必须严格遵守加减的顺序,否则结果会出错

3. 最小公倍数的计算

  • 计算多个数的最小公倍数时,可以使用递归的方式
  • lcm(a,b,c)=lcm(a,lcm(b,c))\text{lcm}(a, b, c) = \text{lcm}(a, \text{lcm}(b, c))

七、总结

通过这道题目,我们可以复习容斥原理在计数问题中的应用,巩固最大公约数和最小公倍数的计算方法。同时,我们也需要注意编程实现中的细节,比如整数除法、容斥原理的正确应用等。

在解决这类问题时,清晰的分析和严谨的数学推导是非常重要的。希望今后在遇到类似的问题时,能够快速地想到使用容斥原理,并正确地应用到解题过程中。