数学-能被整除的数-容斥定理
c++
AcWing 890. 能被整除的数
/*
* 问题描述:
* AcWing 890. 能被整除的数
* 给定一个整数 n 和 m 个不同的质数 p1,p2,…,pm。
* 请你求出 1∼n 中能被 p1,p2,…,pm 中的至少一个数整除的整数有多少个。
* 输入格式
* 第一行包含整数 n 和 m。
* 第二行包含 m 个质数。
* 输出格式
* 输出一个整数,表示满足条件的整数的个数。
* 数据范围
* 1 ≤ m ≤ 16,
* 1 ≤ n, pi ≤ 10 ^ 9
*
* 解题思路:
* 容斥定理:
* |A1 \cup A2 \cup ... \cup An| = |A1| + |A2| + ... + |An|
* - sum_{i, j}(|Ai \cap Aj|)
* + sum_{i, j, k}(|Ai \cap Aj \cap Ak|)
* ...
* 证明思路可以从 a1, a2, ..., ai 几个集合相交的情况下,查看上个公式的计数是否为 1 :
* C(i, 1) - C(i, 2) + ... + C(i, j) * (-1) ^ (j + 1) + ... + C(i, i) * (-1) ^ (i + 1)
* 从 (1 + x) ^ i = 1 + C(i, 1) * x + ... + C(i, j) * x ^ j + ... + C(i, i) * x ^ i
* 取 x = -1
* 可得 C(i, 1) + ... + C(i, j) * (-1) ^ (j + 1) + ... + C(i, i) * (-1) ^ (i + 1) = 1
* 就是说任意几个集合的结合,计数为 1,该定理计数是正确的。
*
* 带入到本题,直接使用状态循环,又因为互质,免得求解最大公因数了。
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int n, m;
int p[25];
LL solution() {
LL res = 0;
int cnt = 0;
int cur = n;
for (int state = 1; state < (1 << m); state ++ ) {
cnt = 0;
cur = n;
for (int i = 0; i < m; i ++ ) {
if ((state >> i) & 1) {
cnt += 1;
cur /= p[i + 1];
}
}
if (cnt % 2 == 1) {
res += cur;
} else {
res -= cur;
}
}
return res;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i ++ ) {
scanf("%d", &p[i]);
}
LL res = solution();
printf("%lld", res);
return 0;
}