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

155 阅读3分钟

问题描述

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

测试样例

样例1:

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

样例2:

输入:a = 5,b = 7,c = 11,l = 15,r = 100
输出:34

样例3:

输入:a = 1,b = 1,c = 1,l = 1,r = 1000
输出:1000

题目分析

暴力做法 - O(r - l)

有一个很容易构想的做法:我们只需要枚举区间[l,r]中的所有数字,分别对a,b,c三个数取模,判断是否为三个数中任何一个数的倍数即可。

时间复杂度O(rl)O(r-l),自信提交,直接TLE。

看起来这种方法是不够快的,得需要一点数学上的思考。

容斥原理 - O(1)

通过观察,题目要求我们计算在区间 [l, r] 内,有多少个数是 a 的倍数,或者是 b 的倍数,或者是 c 的倍数。

一个很自然的思路是:计算区间 [l, r] 内 a 的倍数的个数,b 的倍数的个数, c 的倍数的个数,将他们全部加起来。计算单个区间某个数的倍数很简单,例如x:我们只需要 r/x(l1)/xr / x - (l-1) / x 就是所求答案。

但这样会带来一个新的问题,由于 abc 的倍数可能有重叠,我们需要减去这些重叠的部分。

重叠部分的处理

  • 减去 a 和 b 的倍数的重叠部分(即 lcm(a, b) 的倍数)。
  • 减去 a 和 c 的倍数的重叠部分(即 lcm(a, c) 的倍数)。
  • 减去 b 和 c 的倍数的重叠部分(即 lcm(b, c) 的倍数)。
  • 最后,加上 abc 三者的倍数的重叠部分(即 lcm(lcm(a, b), c) 的倍数)。

这个过程我们将其称为容斥。

在计数时,必须注意没有重复,没有遗漏。为了使重叠部分不被重复计算,人们研究出一种新的计数方法,这种方法的基本思想是:先不考虑重叠的情况,把包含于某内容中的所有对象的数目先计算出来,然后再把计数时重复计算的数目排斥出去,使得计算的结果既无遗漏又无重复,这种计数的方法称为容斥原理。

如果被计数的事物有A、B、C三类,那么,A类和B类和C类元素个数总和 = A类元素个数 + B类元素个数 + C类元素个数 — 既是A类又是B类的元素个数 — 既是A类又是C类的元素个数 — 既是B类又是C类的元素个数 + 既是A类又是B类而且是C类的元素个数。(A∪B∪C = A+B+C - A∩B - B∩C - C∩A + A∩B∩C)

C++代码

typedef long long ll;
int solution(int a, int b, int c, int l, int r) {
    ll ans = 0;
    l --;
    ans += r / a - l / a;
    ans += r / b - l / b;
    ans += r / c - l / c;
    ans -= r / (lcm(a ,b)) - l / (lcm(a ,b));
    ans -= r / (lcm(a ,c)) - l / (lcm(a ,c));
    ans -= r / (lcm(c ,b)) - l / (lcm(c ,b));
    a = lcm(a, b);
    a = lcm(a, c);
    ans += r / a - l / a;
    return ans;
}