问题描述
小U对数字倍数问题很感兴趣,她想知道在区间[l,r][l,r]内,有多少个数是aa的倍数,或者是bb的倍数,或者是cc的倍数。你需要帮小U计算出这些数的个数。
解题思路
- 计算单个数的倍数:首先,我们可以计算在区间
[l, r]内,有多少个数是a的倍数,有多少个数是b的倍数,有多少个数是c的倍数。 - 使用容斥原理:为了避免重复计算,我们需要使用容斥原理来计算这些数的并集。
步骤:
- 计算倍数的数量:
设 f(x)f(x) 为在区间 [1,x][1,x] 中是 nn 的倍数的数字的数量:
f(n,x)=⌊xn⌋f(n,x)=⌊nx⌋
因此,在区间 [l,r][l,r] 中是 nn 的倍数的数字的数量为:
f(n,l,r)=f(n,r)−f(n,l−1)=⌊rn⌋−⌊l−1n⌋f(n,l,r)=f(n,r)−f(n,l−1)=⌊nr⌋−⌊nl−1⌋
2. 使用包含-排除原理:
设 NaNa、NbNb 和 NcNc 分别为区间 [l,r][l,r] 中 aa、bb 和 cc 的倍数的数量。 计算 NaNa、NbNb、NcNc:
- Na=f(a,l,r)Na=f(a,l,r)
- Nb=f(b,l,r)Nb=f(b,l,r)
- Nc=f(c,l,r)Nc=f(c,l,r)
2. 计算两个同事的倍数:
计算 NabNab、NacNac、NbcNbc,其中 NabNab 是同时是 aa 和 bb 的倍数的数量,使用最小公倍数(LCM):
- Nab=f(lcm(a,b),l,r)Nab=f(lcm(a,b),l,r)
- 同理计算 NacNac 和 NbcNbc。
2. 计算三个同事的倍数:
计算 NabcNabc:
- Nabc=f(lcm(a,b,c),l,r)Nabc=f(lcm(a,b,c),l,r)
2. 应用包含-排除原理:
根据包含-排除原理,最终计算数量为:
N=Na+Nb+Nc−Nab−Nac−Nbc+NabcN=Na+Nb+Nc−Nab−Nac−Nbc+Nabc
代码提示
我们可以通过以下步骤来实现这个逻辑:
-
计算单个数的倍数个数:
- 计算
a的倍数个数:countA = (r / a) - ((l - 1) / a) - 计算
b的倍数个数:countB = (r / b) - ((l - 1) / b) - 计算
c的倍数个数:countC = (r / c) - ((l - 1) / c)
- 计算
-
计算两个数的公倍数的个数:
- 计算
a和b的公倍数个数:countAB = (r / lcm(a, b)) - ((l - 1) / lcm(a, b)) - 计算
a和c的公倍数个数:countAC = (r / lcm(a, c)) - ((l - 1) / lcm(a, c)) - 计算
b和c的公倍数个数:countBC = (r / lcm(b, c)) - ((l - 1) / lcm(b, c))
- 计算
-
计算三个数的公倍数的个数:
- 计算
a、b和c的公倍数个数:countABC = (r / lcm(a, b, c)) - ((l - 1) / lcm(a, b, c))
- 计算
-
使用容斥原理计算总数:
- 总数 =
countA + countB + countC - countAB - countAC - countBC + countABC
- 总数 =
代码框架
分别计算单个数的倍数个数、两个数的公倍数的个数、三个数的公倍数的个数以及使用容斥原理计算总数。
//计算单个数的倍数个数
int countA = (r / a) - ((l - 1) / a);
int countB = (r / b) - ((l - 1) / b);
int countC = (r / c) - ((l - 1) / c);
// 计算两个数的公倍数的个数
int countAB = (r / lcm(a, b)) - ((l - 1) / lcm(a, b));
int countAC = (r / lcm(a, c)) - ((l - 1) / lcm(a, c));
int countBC = (r / lcm(b, c)) - ((l - 1) / lcm(b, c));
// 计算三个数的公倍数的个数
int countABC = (r / lcm(a, b, c)) - ((l - 1) / lcm(a, b, c));
// 使用容斥原理计算总数
int totalCount = countA + countB + countC - countAB - countAC - countBC + countABC;
return totalCount;
下面计算两个数的最小公倍数、三个数的最小公倍数、以及两个数的最大公约数
//计算两个数的最小公倍数
public static int lcm(int a, int b) {
return a * (b / gcd(a, b));
}
// 计算三个数的最小公倍数
public static int lcm(int a, int b, int c) {
return lcm(lcm(a, b), c);
}
// 计算两个数的最大公约数
public static int gcd(int a, int b) {
while (b != 0) {
int temp = b;
b = a % b;
a = temp;
}
return a;
}
总结
- 计算单个数的倍数个数:使用整除运算符
/来计算。 - 计算公倍数:使用
lcm函数来计算两个数的最小公倍数,并递归计算三个数的最小公倍数。 - 使用容斥原理:通过加减不同的计数来避免重复计算。