文章标题: 区间倍数问题的容斥原理解法解析
1. 问题描述
在一个区间 [l, r] 内,给定三个整数 a、b 和 c,我们需要计算在这个区间内,能够被 a、b 或 c 整除的数字的个数。即,我们要找出满足以下条件的数的个数:
- 能够被
a整除, - 或者能够被
b整除, - 或者能够被
c整除。
2. 解题思路
这个问题的核心在于计算区间 [l, r] 内,能被 a、b 或 c 整除的数。为了避免重复计数(比如既能被 a 整除,又能被 b 整除的数),我们可以使用 容斥原理 来准确计算。
容斥原理简介
容斥原理(Inclusion-Exclusion Principle)是一个常见的计算问题的方法,它的基本思想是:
- 加法部分:我们先计算每个条件独立的结果。
- 减法部分:我们再减去那些被两个条件同时满足的结果。
- 加法部分:最后加上那些被所有条件同时满足的结果,以避免重复减去。
具体来说,我们需要计算:
- 能被
a、b或c整除的数字的个数。 - 减去能同时被
a和b整除的数字个数。 - 减去能同时被
a和c整除的数字个数。 - 减去能同时被
b和c整除的数字个数。 - 加上能同时被
a、b和c整除的数字个数。
通过容斥原理,我们能准确地计算符合条件的数字个数。
具体步骤
-
计算能被
x整除的数: 对于任意一个数x,我们可以通过r // x - (l - 1) // x计算出在区间[l, r]内,能被x整除的数的个数。 -
使用最小公倍数 (LCM) 处理交集:
- 对于
a和b的交集,实际上就是计算能同时被a和b整除的数,也就是求a和b的最小公倍数(LCM)。 - 以此类推,我们计算能同时被
a和c整除,b和c整除的数,最后计算能同时被a、b和c整除的数。
- 对于
-
公式推导: 最终,我们可以通过如下公式计算结果:
result=count_multiples(a)+count_multiples(b)+count_multiples(c)−count_multiples(lcm(a,b))−count_multiples(lcm(a,c))−count_multiples(lcm(b,c))+count_multiples(lcm(a,b,c))\text{result} = \text{count_multiples}(a) + \text{count_multiples}(b) + \text{count_multiples}(c) - \text{count_multiples}(lcm(a, b)) - \text{count_multiples}(lcm(a, c)) - \text{count_multiples}(lcm(b, c)) + \text{count_multiples}(lcm(a, b, c))
3. 代码实现
import math
# 计算区间[l, r]内,能够被x整除的数的个数
def count_multiples(x, l, r):
return r // x - (l - 1) // x
# 求最小公倍数
def lcm(x, y):
return x * y // math.gcd(x, y)
def solution(a, b, c, l, r):
# 使用容斥原理计算结果
count_a = count_multiples(a, l, r)
count_b = count_multiples(b, l, r)
count_c = count_multiples(c, l, r)
count_ab = count_multiples(lcm(a, b), l, r)
count_ac = count_multiples(lcm(a, c), l, r)
count_bc = count_multiples(lcm(b, c), l, r)
count_abc = count_multiples(lcm(lcm(a, b), c), l, r)
result = count_a + count_b + count_c - count_ab - count_ac - count_bc + count_abc
return result
# 测试样例
print(solution(2, 3, 4, 1, 10)) # 输出: 7
print(solution(5, 7, 11, 15, 100)) # 输出: 34
print(solution(1, 1, 1, 1, 1000)) # 输出: 1000
4. 代码详解
-
count_multiples(x, l, r): 这个函数计算区间[l, r]内,能被x整除的数的个数。通过公式r // x - (l - 1) // x来求解。r // x是r以内能被x整除的数的个数,(l - 1) // x是l-1以内能被x整除的数的个数,差值即为区间[l, r]内的数。 -
lcm(x, y): 该函数计算x和y的最小公倍数,使用公式x * y // gcd(x, y),其中gcd(x, y)是x和y的最大公约数。 -
solution(a, b, c, l, r):- 通过容斥原理,首先分别计算区间内能被
a、b、c整除的数的个数。 - 然后减去能同时被两个数整除的数的个数。
- 最后加上能同时被三个数整除的数的个数。
- 通过容斥原理,首先分别计算区间内能被
5. 图解
为了帮助理解容斥原理的使用,可以通过图示来表示。我们可以用 Venn 图来表示:
- 圆 A 表示能被
a整除的数, - 圆 B 表示能被
b整除的数, - 圆 C 表示能被
c整除的数。
容斥原理告诉我们,计算这些圆的联合时:
- 加上每个圆的面积(能被
a、b、c各自整除的数)。 - 减去两个圆的交集面积(能同时被
a和b整除的数,等等)。 - 最后加上三个圆的交集面积(能同时被
a、b和c整除的数)。
6. 时间复杂度分析
count_multiples(x, l, r):每次计算是常数时间O(1)。lcm(x, y):计算最小公倍数需要计算最大公约数,时间复杂度是O(log(min(x, y)))。- 总的时间复杂度:由于我们需要计算几个
lcm和count_multiples,因此时间复杂度为O(log(max(a, b, c)))。
7. 总结
通过这道题,我们展示了如何使用 容斥原理 来准确计算区间内能被某些数整除的数的个数。该方法避免了重复计算,并且能够高效地处理问题。容斥原理在很多计算交集、并集和补集的问题中都非常有用。通过最小公倍数(LCM)的运算,我们能够有效地计算多个条件交集的情况。此题的解法时间复杂度较低,适用于大范围的输入。