题目分析
我们需要计算在区间 [l, r]
内满足以下条件之一的整数的个数:
- 是
k
的倍数。 - 个位上是
k
。
由于这两个条件可能有重叠(即一个数既是 k
的倍数,个位又是 k
),我们需要使用容斥原理来避免重复计算。
容斥原理
设:
A
为区间[l, r]
内是k
的倍数的数的集合。B
为区间[l, r]
内个位是k
的数的集合。
我们需要计算的是 |A ∪ B|
,即 A
和 B
的并集的大小。根据容斥原理:
∣
A
∪
B
∣
=
∣
A
∣
+
∣
B
∣
−
∣
A
∩
B
∣
|A ∪ B| = |A| + |B| - |A ∩ B|
∣A∪B∣=∣A∣+∣B∣−∣A∩B∣
其中:
|A|
是区间[l, r]
内是k
的倍数的数的个数。|B|
是区间[l, r]
内个位是k
的数的个数。|A ∩ B|
是区间[l, r]
内既是k
的倍数,个位又是k
的数的个数。
计算方法
-
计算
|A|
:- 区间
[l, r]
内是k
的倍数的数的个数为:
∣ A ∣ = ⌊ r k ⌋ − ⌊ l − 1 k ⌋ |A| = \left\lfloor \frac{r}{k} \right\rfloor - \left\lfloor \frac{l-1}{k} \right\rfloor ∣A∣=⌊kr⌋−⌊kl−1⌋
- 区间
-
计算
|B|
:- 个位是
k
的数可以表示为10a + k
,其中a
是任意非负整数。 - 区间
[l, r]
内个位是k
的数的个数为:
∣ B ∣ = ⌊ r − k 10 ⌋ − ⌊ l − 1 − k 10 ⌋ + 1 |B| = \left\lfloor \frac{r - k}{10} \right\rfloor - \left\lfloor \frac{l - 1 - k}{10} \right\rfloor + 1 ∣B∣=⌊10r−k⌋−⌊10l−1−k⌋+1
- 个位是
-
计算
|A ∩ B|
:- 既是
k
的倍数,个位又是k
的数需要满足:
10 a + k ≡ 0 ( m o d k ) 10a + k \equiv 0 \pmod{k} 10a+k≡0(modk)
化简得:
10 a ≡ − k ( m o d k ) ⟹ 10 a ≡ 0 ( m o d k ) 10a \equiv -k \pmod{k} \implies 10a \equiv 0 \pmod{k} 10a≡−k(modk)⟹10a≡0(modk)
因为10a
必须是k
的倍数,所以a
必须是k / \gcd(10, k)
的倍数。 - 设
d = \gcd(10, k)
,则符合条件的数为:
10 ⋅ ( k d ⋅ t ) + k = 10 k d ⋅ t + k 10 \cdot \left( \frac{k}{d} \cdot t \right) + k = \frac{10k}{d} \cdot t + k 10⋅(dk⋅t)+k=d10k⋅t+k
这是一个等差数列,首项为k
,公差为10k / d
。 - 区间
[l, r]
内符合条件的数的个数为:
∣ A ∩ B ∣ = ⌊ r − k 10 k / d ⌋ − ⌊ l − 1 − k 10 k / d ⌋ + 1 |A ∩ B| = \left\lfloor \frac{r - k}{10k / d} \right\rfloor - \left\lfloor \frac{l - 1 - k}{10k / d} \right\rfloor + 1 ∣A∩B∣=⌊10k/dr−k⌋−⌊10k/dl−1−k⌋+1
- 既是
-
最终结果:
- 使用容斥原理计算:
∣ A ∪ B ∣ = ∣ A ∣ + ∣ B ∣ − ∣ A ∩ B ∣ |A ∪ B| = |A| + |B| - |A ∩ B| ∣A∪B∣=∣A∣+∣B∣−∣A∩B∣
- 使用容斥原理计算:
代码实现
以下是 C++ 实现代码:
#include <iostream>
#include <algorithm>
using namespace std;
// 计算最大公约数
int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
// 计算区间 [l, r] 内是 k 的倍数的数的个数
int countMultiples(int l, int r, int k) {
return r / k - (l - 1) / k;
}
// 计算区间 [l, r] 内个位是 k 的数的个数
int countLastDigitK(int l, int r, int k) {
return (r - k) / 10 - (l - 1 - k) / 10 + 1;
}
// 计算区间 [l, r] 内既是 k 的倍数,个位又是 k 的数的个数
int countBoth(int l, int r, int k) {
int d = gcd(10, k);
int step = 10 * k / d;
return (r - k) / step - (l - 1 - k) / step + 1;
}
int main() {
int n, k;
cin >> n >> k;
while (n--) {
int l, r;
cin >> l >> r;
// 计算 |A|, |B|, |A ∩ B|
int multiples = countMultiples(l, r, k);
int lastDigitK = countLastDigitK(l, r, k);
int both = countBoth(l, r, k);
// 使用容斥原理计算 |A ∪ B|
int result = multiples + lastDigitK - both;
cout << result << endl;
}
return 0;
}
代码说明
gcd
函数:计算两个数的最大公约数。countMultiples
函数:计算区间[l, r]
内是k
的倍数的数的个数。countLastDigitK
函数:计算区间[l, r]
内个位是k
的数的个数。countBoth
函数:计算区间[l, r]
内既是k
的倍数,个位又是k
的数的个数。- 主函数:读取输入,调用上述函数计算结果,并输出。
示例运行
输入:
5 3
1 10
11 1000
111 10000
1111 100000
11111 1000000
输出:
3
396
3956
39555
395556
复杂度分析
- 时间复杂度:每个查询的时间复杂度为 O ( 1 ) O(1) O(1),总复杂度为 O ( n ) O(n) O(n)。
- 空间复杂度: O ( 1 ) O(1) O(1),仅使用常数空间。
希望这段代码和解释对你有帮助!