容斥原理
定理: 共有n个集合,它们的并可以表示为如下关系
∣
A
1
∪
A
2
∪
.
.
.
∪
A
n
∣
=
∑
i
=
1
n
(
−
1
)
i
−
1
∑
i
个
数
的
组
合
∣
A
p
1
∩
A
p
2
∩
.
.
.
∩
A
p
i
∣
|A_{1} \cup A_{2} \cup...\cup A_{n}| = \sum_{i=1}^n{(-1)^{i-1}\sum_{i个数的组合}{|A_{p_{1}} \cap A_{p_{2}} \cap...\cap A_{p_{i}}|}}
∣A1∪A2∪...∪An∣=∑i=1n(−1)i−1∑i个数的组合∣Ap1∩Ap2∩...∩Api∣
证明:
goal:对任意一个元素,它在左式和右式都只被计算了一次
设某个元素被k个集合包含,显然地,其对左式的贡献为1,因为在并集中只计算一次。
接下来证明它在右式被计算的次数也为1
- i = 1时被计算了k次
- i = 2时被计算了 C k i C_{k}^{i} Cki 当然,此时为负数
依次类推,可得到计算次数为
∑
i
=
1
k
C
k
i
(
−
1
)
i
+
1
=
−
∑
i
=
1
k
C
k
i
(
−
1
)
i
\sum_{i=1}^{k}{C_{k}^i(-1)^{i+1}} = -\sum_{i=1}^{k}{C_{k}^i(-1)^{i}}
∑i=1kCki(−1)i+1=−∑i=1kCki(−1)i,这是
−
(
1
−
x
)
k
+
C
k
0
-(1-x)^k+C_{k}^{0}
−(1−x)k+Ck0令
x
=
1
x=1
x=1之后的展开式,显然结果为1。
再由k的任意性,可以得到结论,证毕
原题链接:能被整除的数
给定一个整数 n 和 m 个不同的质数 p1,p2,…,pm。
请你求出 1∼n 中能被 p1,p2,…,pm 中的至少一个数整除的整数有多少个。
#include <iostream>
using namespace std;
/*
思路:至少被一个整除,相当于求整除的并,此时就就可以使用容斥原理了
1. s_{i} - s_{i, j} + s_{i, j, k} -...+... (其中s_{i, j}表示n中同时被pi和pj整除的数)
2. 枚举出每个子集,计算s,并判断加减,奇数加,偶数减
Tip:1~n中能被pi整除的数的个数就是 n / pi(这是下取整的)因为pi与pj都是不同的质数,n / (pi * pj)就是s_{i, j}的结果
*/
const int N = 20;
int n, m;
int p[N];
// 计算x所代表的集合的元素个数
int cal(int x, int dis) {
int res = n, flag = 0;
for (int i = 0; i < dis; i ++ ) {
if (x >> i & 1) {
flag ++ ;
res /= p[i];
}
}
//cout << res << endl;
return flag % 2 == 0 ? -res : res;
}
int main() {
cin >> n >> m;
for (int i = 0; i < m; i ++ ) cin >> p[i];
int res = 0;
// 枚举所有子集,每个pi只有选和不选两种情况
for (int i = 1; i < (1 << m); i ++ ) {
res += cal(i, m);
}
cout << res << endl;
return 0;
}