小U对数字倍数问题很感兴趣,她想知道在区间 内,有多少个数是 的倍数,或者是 的倍数,或者是 的倍数。需要计算出这些数的个数。
示例:
- 输入:
- 输出:7
二、解题思路
1. 分析问题
这个问题要求统计在给定区间内,满足以下任一条件的整数的数量:
- 是 的倍数
- 是 的倍数
- 是 的倍数
需要注意的是,一个数可能同时是多个数的倍数,例如, 既是 的倍数,也是 的倍数。
2. 避免重复计数
为了准确地统计符合条件的整数个数,必须避免重复计数。因此,我们可以使用 容斥原理 来解决这个问题。
3. 容斥原理
容斥原理用于计算多个集合的并集的大小,公式如下:
其中:
- 表示集合 的元素个数
- 表示集合 和 的交集的元素个数
在本题中:
- :所有是 的倍数的数
- :所有是 的倍数的数
- :所有是 的倍数的数
三、算法步骤
1. 计算单独倍数的数的个数
计算在区间 内是 、、 的倍数的数的个数。
公式:
2. 计算两两的最小公倍数(LCM)及其倍数的个数
计算 和 的最小公倍数 ,以及 、,然后计算这些最小公倍数在区间内的倍数的个数。
3. 计算三个数的最小公倍数及其倍数的个数
计算 、、 三个数的最小公倍数 ,计算其在区间内的倍数的个数。
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):
- 计算最小公倍数(LCM):
- 总的计算次数:常数次计算,时间复杂度为
2. 空间复杂度
- 仅使用了常数个变量,空间复杂度为
六、注意事项
1. 整数除法与下取整
- 在 Python 中,使用
//进行整除运算,结果为向下取整的整数 - 避免使用
/,因为其结果为浮点数
2. 容斥原理的加减顺序
- 按照容斥原理的公式,必须严格遵守加减的顺序,否则结果会出错
3. 最小公倍数的计算
- 计算多个数的最小公倍数时,可以使用递归的方式
七、总结
通过这道题目,我们可以复习容斥原理在计数问题中的应用,巩固最大公约数和最小公倍数的计算方法。同时,我们也需要注意编程实现中的细节,比如整数除法、容斥原理的正确应用等。
在解决这类问题时,清晰的分析和严谨的数学推导是非常重要的。希望今后在遇到类似的问题时,能够快速地想到使用容斥原理,并正确地应用到解题过程中。