稀土掘金AI刷题第170题:小C的数字倍数问题|豆包MarsCode AI刷题

142 阅读5分钟

文章标题: 区间倍数问题的容斥原理解法解析

1. 问题描述

在一个区间 [l, r] 内,给定三个整数 abc,我们需要计算在这个区间内,能够被 abc 整除的数字的个数。即,我们要找出满足以下条件的数的个数:

  • 能够被 a 整除,
  • 或者能够被 b 整除,
  • 或者能够被 c 整除。

2. 解题思路

这个问题的核心在于计算区间 [l, r] 内,能被 abc 整除的数。为了避免重复计数(比如既能被 a 整除,又能被 b 整除的数),我们可以使用 容斥原理 来准确计算。

容斥原理简介

容斥原理(Inclusion-Exclusion Principle)是一个常见的计算问题的方法,它的基本思想是:

  • 加法部分:我们先计算每个条件独立的结果。
  • 减法部分:我们再减去那些被两个条件同时满足的结果。
  • 加法部分:最后加上那些被所有条件同时满足的结果,以避免重复减去。

具体来说,我们需要计算:

  1. 能被 abc 整除的数字的个数
  2. 减去能同时被 ab 整除的数字个数
  3. 减去能同时被 ac 整除的数字个数
  4. 减去能同时被 bc 整除的数字个数
  5. 加上能同时被 abc 整除的数字个数

通过容斥原理,我们能准确地计算符合条件的数字个数。

具体步骤

  1. 计算能被 x 整除的数: 对于任意一个数 x,我们可以通过 r // x - (l - 1) // x 计算出在区间 [l, r] 内,能被 x 整除的数的个数。

  2. 使用最小公倍数 (LCM) 处理交集

    • 对于 ab 的交集,实际上就是计算能同时被 ab 整除的数,也就是求 ab 的最小公倍数(LCM)。
    • 以此类推,我们计算能同时被 ac 整除,bc 整除的数,最后计算能同时被 abc 整除的数。
  3. 公式推导: 最终,我们可以通过如下公式计算结果:

    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. 代码详解

  1. count_multiples(x, l, r) : 这个函数计算区间 [l, r] 内,能被 x 整除的数的个数。通过公式 r // x - (l - 1) // x 来求解。r // xr 以内能被 x 整除的数的个数,(l - 1) // xl-1 以内能被 x 整除的数的个数,差值即为区间 [l, r] 内的数。

  2. lcm(x, y) : 该函数计算 xy 的最小公倍数,使用公式 x * y // gcd(x, y),其中 gcd(x, y)xy 的最大公约数。

  3. solution(a, b, c, l, r)

    • 通过容斥原理,首先分别计算区间内能被 abc 整除的数的个数。
    • 然后减去能同时被两个数整除的数的个数。
    • 最后加上能同时被三个数整除的数的个数。

5. 图解

为了帮助理解容斥原理的使用,可以通过图示来表示。我们可以用 Venn 图来表示:

  • 圆 A 表示能被 a 整除的数,
  • 圆 B 表示能被 b 整除的数,
  • 圆 C 表示能被 c 整除的数。

容斥原理告诉我们,计算这些圆的联合时:

  1. 加上每个圆的面积(能被 abc 各自整除的数)。
  2. 减去两个圆的交集面积(能同时被 ab 整除的数,等等)。
  3. 最后加上三个圆的交集面积(能同时被 abc 整除的数)。

6. 时间复杂度分析

  • count_multiples(x, l, r) :每次计算是常数时间 O(1)
  • lcm(x, y) :计算最小公倍数需要计算最大公约数,时间复杂度是 O(log(min(x, y)))
  • 总的时间复杂度:由于我们需要计算几个 lcmcount_multiples,因此时间复杂度为 O(log(max(a, b, c)))

7. 总结

通过这道题,我们展示了如何使用 容斥原理 来准确计算区间内能被某些数整除的数的个数。该方法避免了重复计算,并且能够高效地处理问题。容斥原理在很多计算交集、并集和补集的问题中都非常有用。通过最小公倍数(LCM)的运算,我们能够有效地计算多个条件交集的情况。此题的解法时间复杂度较低,适用于大范围的输入。