数学-能被整除的数-容斥定理

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;
}

posted @ 2022-07-28 20:54  lucky_light  阅读(159)  评论(0编辑  收藏  举报