容斥原理(Inclusion-Exclusion Principle)是组合数学中一个非常重要的工具,用于计算多个集合的并集大小。它的核心思想是通过“加加减减”的方式,避免重复计数,从而准确地求出多个集合的并集大小。本文将从3个元素的容斥原理出发,逐步推导出n个元素的容斥原理公式,并给出直观的解释。
1. 从3个元素开始
假设我们有3个集合 A A A, B B B, C C C,我们希望计算它们的并集大小 ∣ A ∪ B ∪ C ∣ |A \cup B \cup C| ∣A∪B∪C∣。
直观理解
-
首先,我们简单地将每个集合的大小相加:
∣ A ∣ + ∣ B ∣ + ∣ C ∣ . |A| + |B| + |C|. ∣A∣+∣B∣+∣C∣.
但是,这样会重复计算 A ∩ B A \cap B A∩B, A ∩ C A \cap C A∩C, B ∩ C B \cap C B∩C 的部分。 -
为了修正重复计算的部分,我们减去两两交集的大小:
∣ A ∣ + ∣ B ∣ + ∣ C ∣ − ∣ A ∩ B ∣ − ∣ A ∩ C ∣ − ∣ B ∩ C ∣ . |A| + |B| + |C| - |A \cap B| - |A \cap C| - |B \cap C|. ∣A∣+∣B∣+∣C∣−∣A∩B∣−∣A∩C∣−∣B∩C∣.
但是,这样又会过度减去 A ∩ B ∩ C A \cap B \cap C A∩B∩C 的部分。 -
最后,我们需要将 A ∩ B ∩ C A \cap B \cap C A∩B∩C 的部分加回来:
∣ A ∣ + ∣ B ∣ + ∣ C ∣ − ∣ A ∩ B ∣ − ∣ A ∩ C ∣ − ∣ B ∩ C ∣ + ∣ A ∩ B ∩ C ∣ . |A| + |B| + |C| - |A \cap B| - |A \cap C| - |B \cap C| + |A \cap B \cap C|. ∣A∣+∣B∣+∣C∣−∣A∩B∣−∣A∩C∣−∣B∩C∣+∣A∩B∩C∣.
公式总结
3个集合的容斥原理公式为:
∣
A
∪
B
∪
C
∣
=
∣
A
∣
+
∣
B
∣
+
∣
C
∣
−
∣
A
∩
B
∣
−
∣
A
∩
C
∣
−
∣
B
∩
C
∣
+
∣
A
∩
B
∩
C
∣
.
|A \cup B \cup C| = |A| + |B| + |C| - |A \cap B| - |A \cap C| - |B \cap C| + |A \cap B \cap C|.
∣A∪B∪C∣=∣A∣+∣B∣+∣C∣−∣A∩B∣−∣A∩C∣−∣B∩C∣+∣A∩B∩C∣.
2. 推广到n个集合
现在,我们希望将容斥原理推广到n个集合 A 1 , A 2 , … , A n A_1, A_2, \dots, A_n A1,A2,…,An,计算它们的并集大小 ∣ A 1 ∪ A 2 ∪ ⋯ ∪ A n ∣ |A_1 \cup A_2 \cup \dots \cup A_n| ∣A1∪A2∪⋯∪An∣。
直观理解
-
第一步:加上所有单个集合的大小:
∑ i = 1 n ∣ A i ∣ . \sum_{i=1}^n |A_i|. i=1∑n∣Ai∣.
这样会重复计算两两交集的部分。 -
第二步:减去所有两两交集的大小:
− ∑ 1 ≤ i < j ≤ n ∣ A i ∩ A j ∣ . -\sum_{1 \leq i < j \leq n} |A_i \cap A_j|. −1≤i<j≤n∑∣Ai∩Aj∣.
这样会过度减去三个集合交集的部分。 -
第三步:加上所有三个集合交集的大小:
+ ∑ 1 ≤ i < j < k ≤ n ∣ A i ∩ A j ∩ A k ∣ . +\sum_{1 \leq i < j < k \leq n} |A_i \cap A_j \cap A_k|. +1≤i<j<k≤n∑∣Ai∩Aj∩Ak∣.
这样又会重复计算四个集合交集的部分。 -
依此类推:
- 对于奇数个集合的交集,我们加上它们的大小。
- 对于偶数个集合的交集,我们减去它们的大小。
公式总结
n个集合的容斥原理公式为:
∣
A
1
∪
A
2
∪
⋯
∪
A
n
∣
=
∑
i
=
1
n
∣
A
i
∣
−
∑
1
≤
i
<
j
≤
n
∣
A
i
∩
A
j
∣
+
∑
1
≤
i
<
j
<
k
≤
n
∣
A
i
∩
A
j
∩
A
k
∣
−
⋯
+
(
−
1
)
n
+
1
∣
A
1
∩
A
2
∩
⋯
∩
A
n
∣
.
|A_1 \cup A_2 \cup \dots \cup A_n| = \sum_{i=1}^n |A_i| - \sum_{1 \leq i < j \leq n} |A_i \cap A_j| + \sum_{1 \leq i < j < k \leq n} |A_i \cap A_j \cap A_k| - \dots + (-1)^{n+1} |A_1 \cap A_2 \cap \dots \cap A_n|.
∣A1∪A2∪⋯∪An∣=i=1∑n∣Ai∣−1≤i<j≤n∑∣Ai∩Aj∣+1≤i<j<k≤n∑∣Ai∩Aj∩Ak∣−⋯+(−1)n+1∣A1∩A2∩⋯∩An∣.
用求和符号表示为:
∣
A
1
∪
A
2
∪
⋯
∪
A
n
∣
=
∑
k
=
1
n
(
−
1
)
k
+
1
∑
1
≤
i
1
<
i
2
<
⋯
<
i
k
≤
n
∣
A
i
1
∩
A
i
2
∩
⋯
∩
A
i
k
∣
.
|A_1 \cup A_2 \cup \dots \cup A_n| = \sum_{k=1}^n (-1)^{k+1} \sum_{1 \leq i_1 < i_2 < \dots < i_k \leq n} |A_{i_1} \cap A_{i_2} \cap \dots \cap A_{i_k}|.
∣A1∪A2∪⋯∪An∣=k=1∑n(−1)k+11≤i1<i2<⋯<ik≤n∑∣Ai1∩Ai2∩⋯∩Aik∣.
3. 公式推导
我们可以通过数学归纳法证明n个集合的容斥原理公式。
归纳基础
- 当
n
=
1
n = 1
n=1 时,公式显然成立:
∣ A 1 ∣ = ∣ A 1 ∣ . |A_1| = |A_1|. ∣A1∣=∣A1∣. - 当
n
=
2
n = 2
n=2 时,公式为:
∣ A 1 ∪ A 2 ∣ = ∣ A 1 ∣ + ∣ A 2 ∣ − ∣ A 1 ∩ A 2 ∣ . |A_1 \cup A_2| = |A_1| + |A_2| - |A_1 \cap A_2|. ∣A1∪A2∣=∣A1∣+∣A2∣−∣A1∩A2∣. 这也是容斥原理的基本形式。
归纳假设
假设对于
n
=
m
n = m
n=m,容斥原理公式成立,即:
∣
A
1
∪
A
2
∪
⋯
∪
A
m
∣
=
∑
k
=
1
m
(
−
1
)
k
+
1
∑
1
≤
i
1
<
i
2
<
⋯
<
i
k
≤
m
∣
A
i
1
∩
A
i
2
∩
⋯
∩
A
i
k
∣
.
|A_1 \cup A_2 \cup \dots \cup A_m| = \sum_{k=1}^m (-1)^{k+1} \sum_{1 \leq i_1 < i_2 < \dots < i_k \leq m} |A_{i_1} \cap A_{i_2} \cap \dots \cap A_{i_k}|.
∣A1∪A2∪⋯∪Am∣=k=1∑m(−1)k+11≤i1<i2<⋯<ik≤m∑∣Ai1∩Ai2∩⋯∩Aik∣.
归纳步骤
对于
n
=
m
+
1
n = m + 1
n=m+1,我们需要证明:
∣
A
1
∪
A
2
∪
⋯
∪
A
m
+
1
∣
=
∣
A
1
∪
A
2
∪
⋯
∪
A
m
∣
+
∣
A
m
+
1
∣
−
∣
(
A
1
∪
A
2
∪
⋯
∪
A
m
)
∩
A
m
+
1
∣
.
|A_1 \cup A_2 \cup \dots \cup A_{m+1}| = |A_1 \cup A_2 \cup \dots \cup A_m| + |A_{m+1}| - |(A_1 \cup A_2 \cup \dots \cup A_m) \cap A_{m+1}|.
∣A1∪A2∪⋯∪Am+1∣=∣A1∪A2∪⋯∪Am∣+∣Am+1∣−∣(A1∪A2∪⋯∪Am)∩Am+1∣.
根据归纳假设,将
∣
A
1
∪
A
2
∪
⋯
∪
A
m
∣
|A_1 \cup A_2 \cup \dots \cup A_m|
∣A1∪A2∪⋯∪Am∣ 和
∣
(
A
1
∪
A
2
∪
⋯
∪
A
m
)
∩
A
m
+
1
∣
|(A_1 \cup A_2 \cup \dots \cup A_m) \cap A_{m+1}|
∣(A1∪A2∪⋯∪Am)∩Am+1∣ 展开,即可得到
n
=
m
+
1
n = m + 1
n=m+1 的容斥原理公式。
4. 应用示例
假设有4个集合
A
1
,
A
2
,
A
3
,
A
4
A_1, A_2, A_3, A_4
A1,A2,A3,A4,它们的并集大小为:
∣
A
1
∪
A
2
∪
A
3
∪
A
4
∣
=
∣
A
1
∣
+
∣
A
2
∣
+
∣
A
3
∣
+
∣
A
4
∣
−
∣
A
1
∩
A
2
∣
−
∣
A
1
∩
A
3
∣
−
∣
A
1
∩
A
4
∣
−
∣
A
2
∩
A
3
∣
−
∣
A
2
∩
A
4
∣
−
∣
A
3
∩
A
4
∣
+
∣
A
1
∩
A
2
∩
A
3
∣
+
∣
A
1
∩
A
2
∩
A
4
∣
+
∣
A
1
∩
A
3
∩
A
4
∣
+
∣
A
2
∩
A
3
∩
A
4
∣
−
∣
A
1
∩
A
2
∩
A
3
∩
A
4
∣
.
|A_1 \cup A_2 \cup A_3 \cup A_4| = |A_1| + |A_2| + |A_3| + |A_4| - |A_1 \cap A_2| - |A_1 \cap A_3| - |A_1 \cap A_4| - |A_2 \cap A_3| - |A_2 \cap A_4| - |A_3 \cap A_4| + |A_1 \cap A_2 \cap A_3| + |A_1 \cap A_2 \cap A_4| + |A_1 \cap A_3 \cap A_4| + |A_2 \cap A_3 \cap A_4| - |A_1 \cap A_2 \cap A_3 \cap A_4|.
∣A1∪A2∪A3∪A4∣=∣A1∣+∣A2∣+∣A3∣+∣A4∣−∣A1∩A2∣−∣A1∩A3∣−∣A1∩A4∣−∣A2∩A3∣−∣A2∩A4∣−∣A3∩A4∣+∣A1∩A2∩A3∣+∣A1∩A2∩A4∣+∣A1∩A3∩A4∣+∣A2∩A3∩A4∣−∣A1∩A2∩A3∩A4∣.
5. 总结
容斥原理通过交替加减集合交集的大小,避免了重复计数,从而准确地计算多个集合的并集大小。从3个集合的简单情况出发,我们可以推广到n个集合的一般情况,并通过数学归纳法严格证明其正确性。容斥原理在概率论、组合数学、计算机科学等领域有着广泛的应用,是解决复杂计数问题的有力工具。
6.练习
问题描述
给定一个整数 n n n 和 m m m 个不同的质数 p 1 , p 2 , … , p m p_1, p_2, \dots, p_m p1,p2,…,pm,要求求出 1 ∼ n 1 \sim n 1∼n 中能被至少一个质数整除的整数有多少个。
解题思路
这个问题可以通过容斥原理来解决。容斥原理的核心思想是通过“加加减减”的方式,避免重复计数,从而准确地计算多个集合的并集大小。
1. 问题转化
- 设 A i A_i Ai 表示 1 ∼ n 1 \sim n 1∼n 中能被 p i p_i pi 整除的整数的集合。
- 我们需要计算的是:
∣ A 1 ∪ A 2 ∪ ⋯ ∪ A m ∣ , |A_1 \cup A_2 \cup \dots \cup A_m|, ∣A1∪A2∪⋯∪Am∣,
即 1 ∼ n 1 \sim n 1∼n 中能被至少一个质数整除的整数的个数。
2. 容斥原理公式
根据容斥原理,多个集合的并集大小可以通过以下公式计算:
∣
A
1
∪
A
2
∪
⋯
∪
A
m
∣
=
∑
i
=
1
m
∣
A
i
∣
−
∑
1
≤
i
<
j
≤
m
∣
A
i
∩
A
j
∣
+
∑
1
≤
i
<
j
<
k
≤
m
∣
A
i
∩
A
j
∩
A
k
∣
−
⋯
+
(
−
1
)
m
+
1
∣
A
1
∩
A
2
∩
⋯
∩
A
m
∣
.
|A_1 \cup A_2 \cup \dots \cup A_m| = \sum_{i=1}^m |A_i| - \sum_{1 \leq i < j \leq m} |A_i \cap A_j| + \sum_{1 \leq i < j < k \leq m} |A_i \cap A_j \cap A_k| - \dots + (-1)^{m+1} |A_1 \cap A_2 \cap \dots \cap A_m|.
∣A1∪A2∪⋯∪Am∣=i=1∑m∣Ai∣−1≤i<j≤m∑∣Ai∩Aj∣+1≤i<j<k≤m∑∣Ai∩Aj∩Ak∣−⋯+(−1)m+1∣A1∩A2∩⋯∩Am∣.
3. 计算交集大小
- 对于任意
k
k
k 个质数
p
i
1
,
p
i
2
,
…
,
p
i
k
p_{i_1}, p_{i_2}, \dots, p_{i_k}
pi1,pi2,…,pik,它们的交集大小为:
∣ A i 1 ∩ A i 2 ∩ ⋯ ∩ A i k ∣ = ⌊ n lcm ( p i 1 , p i 2 , … , p i k ) ⌋ . |A_{i_1} \cap A_{i_2} \cap \dots \cap A_{i_k}| = \left\lfloor \frac{n}{\text{lcm}(p_{i_1}, p_{i_2}, \dots, p_{i_k})} \right\rfloor. ∣Ai1∩Ai2∩⋯∩Aik∣=⌊lcm(pi1,pi2,…,pik)n⌋.由于 p i p_i pi 是质数,且互不相同,因此:
lcm ( p i 1 , p i 2 , … , p i k ) = p i 1 ⋅ p i 2 ⋅ ⋯ ⋅ p i k . \text{lcm}(p_{i_1}, p_{i_2}, \dots, p_{i_k}) = p_{i_1} \cdot p_{i_2} \cdot \dots \cdot p_{i_k}. lcm(pi1,pi2,…,pik)=pi1⋅pi2⋅⋯⋅pik.
4. 二进制枚举
为了高效地计算所有可能的交集,我们可以使用二进制枚举的方法:
- 对于 m m m 个质数,共有 2 m − 1 2^m - 1 2m−1 种非空子集。
- 每个子集对应一个交集,其符号由子集的大小决定:
- 如果子集大小为奇数,则符号为正。
- 如果子集大小为偶数,则符号为负。
代码实现
以下是基于容斥原理和二进制枚举的 Python 与 c++ 实现:
def count_divisible_numbers(n, primes):
m = len(primes)
total = 0
# 遍历所有非空子集
for mask in range(1, 1 << m):
bits = bin(mask).count('1') # 子集大小
lcm = 1
for i in range(m):
if mask & (1 << i): # 第 i 个质数在子集中
lcm *= primes[i]
count = n // lcm # 交集大小
if bits % 2 == 1: # 奇数子集,符号为正
total += count
else: # 偶数子集,符号为负
total -= count
return total
#include <iostream>
using namespace std;
// 主函数
int main() {
// 提高I/O效率
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m; // n为上限值,m为数组p的长度
cin >> n >> m;
int primes[30]; // 存储输入的质数
for (int i = 0; i < m; i++) {
cin >> primes[i];
}
int result = 0; // 最终结果
// 遍历所有可能的子集(使用位掩码)
for (int subsetMask = 1; subsetMask <= (1 << m) - 1; subsetMask++) {
int countSelectedPrimes = 0; // 当前子集中选择的质数个数
long long productOfPrimes = 1; // 当前子集中质数的乘积
bool isProductValid = true; // 标记当前子集的乘积是否有效
// 检查每个质数是否在当前子集中
for (int j = 0; j < m; j++) {
if ((subsetMask >> j) & 1) { // 如果第j位被选中
// 检查乘积是否会超过n
if ((productOfPrimes * primes[j]) > n) {
isProductValid = false;
break;
}
productOfPrimes *= primes[j]; // 更新乘积
countSelectedPrimes++; // 增加计数
}
}
// 如果乘积无效,跳过该子集
if (!isProductValid) continue;
// 根据选择的质数个数决定是加还是减
if (countSelectedPrimes % 2 == 1) {
result += n / productOfPrimes;
} else {
result -= n / productOfPrimes;
}
}
// 输出最终结果
cout << result << endl;
return 0;
}
示例
输入:
n = 20
primes = [2, 3, 5]
计算过程:
-
单个质数的交集:
- ∣ A 1 ∣ = ⌊ 20 / 2 ⌋ = 10 |A_1| = \lfloor 20 / 2 \rfloor = 10 ∣A1∣=⌊20/2⌋=10
- ∣ A 2 ∣ = ⌊ 20 / 3 ⌋ = 6 |A_2| = \lfloor 20 / 3 \rfloor = 6 ∣A2∣=⌊20/3⌋=6
- ∣ A 3 ∣ = ⌊ 20 / 5 ⌋ = 4 |A_3| = \lfloor 20 / 5 \rfloor = 4 ∣A3∣=⌊20/5⌋=4
-
两两质数的交集:
- ∣ A 1 ∩ A 2 ∣ = ⌊ 20 / 6 ⌋ = 3 |A_1 \cap A_2| = \lfloor 20 / 6 \rfloor = 3 ∣A1∩A2∣=⌊20/6⌋=3
- ∣ A 1 ∩ A 3 ∣ = ⌊ 20 / 10 ⌋ = 2 |A_1 \cap A_3| = \lfloor 20 / 10 \rfloor = 2 ∣A1∩A3∣=⌊20/10⌋=2
- ∣ A 2 ∩ A 3 ∣ = ⌊ 20 / 15 ⌋ = 1 |A_2 \cap A_3| = \lfloor 20 / 15 \rfloor = 1 ∣A2∩A3∣=⌊20/15⌋=1
-
三个质数的交集:
- ∣ A 1 ∩ A 2 ∩ A 3 ∣ = ⌊ 20 / 30 ⌋ = 0 |A_1 \cap A_2 \cap A_3| = \lfloor 20 / 30 \rfloor = 0 ∣A1∩A2∩A3∣=⌊20/30⌋=0
-
应用容斥原理:
∣ A 1 ∪ A 2 ∪ A 3 ∣ = 10 + 6 + 4 − 3 − 2 − 1 + 0 = 14. |A_1 \cup A_2 \cup A_3| = 10 + 6 + 4 - 3 - 2 - 1 + 0 = 14. ∣A1∪A2∪A3∣=10+6+4−3−2−1+0=14.
输出:
14
复杂度分析
-
时间复杂度:
- 二进制枚举的子集总数为 2 m − 1 2^m - 1 2m−1。
- 对于每个子集,计算交集大小的时间复杂度为 O ( m ) O(m) O(m)。
- 总时间复杂度为 O ( m ⋅ 2 m ) O(m \cdot 2^m) O(m⋅2m)。
-
空间复杂度:
- 只需要常数空间存储中间结果,空间复杂度为 O ( 1 ) O(1) O(1)。
总结
通过容斥原理和二进制枚举,我们可以高效地计算 1 ∼ n 1 \sim n 1∼n 中能被至少一个质数整除的整数的个数。这种方法不仅适用于质数,还可以推广到任意一组数的倍数计数问题。容斥原理是解决复杂计数问题的强大工具,而二进制枚举则提供了一种高效的实现方式。