问题描述
小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
解题思路
-
直接计数法:
- 遍历区间
[l, r]
内的每一个数,检查它是否是a
、b
或c
的倍数。 - 这种方法的时间复杂度是
O(n)
,其中n = r - l + 1
。对于较大的区间,这种方法可能会比较慢。
- 遍历区间
-
数学方法:
- 计算区间
[l, r]
内a
的倍数的个数。 - 计算区间
[l, r]
内b
的倍数的个数。 - 计算区间
[l, r]
内c
的倍数的个数。 - 使用容斥原理来避免重复计数:
- 计算
a
和b
的公倍数的个数。 - 计算
a
和c
的公倍数的个数。 - 计算
b
和c
的公倍数的个数。 - 计算
a
、b
和c
的公倍数的个数。
- 计算
- 最终结果是上述所有计数的总和,减去重复计数的部分。
- 计算区间
数据结构选择
- 不需要额外的数据结构,只需要基本的数学运算。
算法步骤
- 计算
a
、b
、c
在区间[l, r]
内的倍数个数。- 计算
a
和b
、a
和c
、b
和c
的公倍数个数。- 计算
a
、b
和c
的公倍数个数。- 使用容斥原理计算最终结果。
代码实现1:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm> // for std::gcd
#include <numeric> // for std::gcd in C++17
using namespace std;
// 计算在区间 [l, r] 内 n 的倍数的个数
int countMultiples(int n, int l, int r) {
return r / n - (l - 1) / n;
}
// 计算两个数的最小公倍数
int lcm(int a, int b) {
return abs(a * b) / std::gcd(a, b);
}
int solution(int a, int b, int c, int l, int r) {
// 计算 a 的倍数个数
int countA = countMultiples(a, l, r);
// 计算 b 的倍数个数
int countB = countMultiples(b, l, r);
// 计算 c 的倍数个数
int countC = countMultiples(c, l, r);
// 计算 a 和 b 的公倍数个数
int countAB = countMultiples(lcm(a, b), l, r);
// 计算 a 和 c 的公倍数个数
int countAC = countMultiples(lcm(a, c), l, r);
// 计算 b 和 c 的公倍数个数
int countBC = countMultiples(lcm(b, c), l, r);
// 计算 a、b 和 c 的公倍数个数
int countABC = countMultiples(lcm(lcm(a, b), c), l, r);
// 使用容斥原理计算最终结果
int result = countA + countB + countC - countAB - countAC - countBC + countABC;
return result;
}
int main() {
std::cout << (solution(2, 3, 4, 1, 10) == 7) << std::endl;
std::cout << (solution(5, 7, 11, 15, 100) == 34) << std::endl;
std::cout << (solution(1, 1, 1, 1, 1000) == 1000) << std::endl;
return 0;
}
代码实现2:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm> // for std::abs
using namespace std;
// 计算两个数的最大公约数
int gcd(int a, int b) {
while (b != 0) {
int temp = b;
b = a % b;
a = temp;
}
return a;
}
// 计算在区间 [l, r] 内 n 的倍数的个数
int countMultiples(int n, int l, int r) {
return r / n - (l - 1) / n;
}
// 计算两个数的最小公倍数
int lcm(int a, int b) {
return abs(a * b) / gcd(a, b);
}
int solution(int a, int b, int c, int l, int r) {
// 计算 a 的倍数个数
int countA = countMultiples(a, l, r);
// 计算 b 的倍数个数
int countB = countMultiples(b, l, r);
// 计算 c 的倍数个数
int countC = countMultiples(c, l, r);
// 计算 a 和 b 的公倍数个数
int countAB = countMultiples(lcm(a, b), l, r);
// 计算 a 和 c 的公倍数个数
int countAC = countMultiples(lcm(a, c), l, r);
// 计算 b 和 c 的公倍数个数
int countBC = countMultiples(lcm(b, c), l, r);
// 计算 a、b 和 c 的公倍数个数
int countABC = countMultiples(lcm(lcm(a, b), c), l, r);
// 使用容斥原理计算最终结果
int result = countA + countB + countC - countAB - countAC - countBC + countABC;
return result;
}
int main() {
std::cout << (solution(2, 3, 4, 1, 10) == 7) << std::endl;
std::cout << (solution(5, 7, 11, 15, 100) == 34) << std::endl;
std::cout << (solution(1, 1, 1, 1, 1000) == 1000) << std::endl;
return 0;
}
解释
-
gcd
函数:- 使用欧几里得算法计算两个数的最大公约数。
- 算法步骤:
- 当
b
不为 0 时,将b
赋值给a
,将a % b
赋值给b
,重复此过程。 - 当
b
为 0 时,a
即为最大公约数。
- 当
-
lcm
函数:- 使用
gcd
函数计算最小公倍数。 - 公式为:
lcm(a, b) = abs(a * b) / gcd(a, b)
。
- 使用