问题描述
小U对数字倍数问题很感兴趣,她想知道在区间内,有多少个数是的倍数,或者是的倍数,或者是的倍数。你需要帮小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三个数取模,判断是否为三个数中任何一个数的倍数即可。
时间复杂度,自信提交,直接TLE。
看起来这种方法是不够快的,得需要一点数学上的思考。
容斥原理 - O(1)
通过观察,题目要求我们计算在区间 [l, r] 内,有多少个数是 a 的倍数,或者是 b 的倍数,或者是 c 的倍数。
一个很自然的思路是:计算区间 [l, r] 内 a 的倍数的个数,b 的倍数的个数, c 的倍数的个数,将他们全部加起来。计算单个区间某个数的倍数很简单,例如x:我们只需要 就是所求答案。
但这样会带来一个新的问题,由于 a、b、c 的倍数可能有重叠,我们需要减去这些重叠的部分。
重叠部分的处理:
- 减去
a和b的倍数的重叠部分(即lcm(a, b)的倍数)。 - 减去
a和c的倍数的重叠部分(即lcm(a, c)的倍数)。 - 减去
b和c的倍数的重叠部分(即lcm(b, c)的倍数)。 - 最后,加上
a、b、c三者的倍数的重叠部分(即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;
}