容斥原理(能被整除的数)

在这里插入图片描述

能被整除的数

给定一个整数 n 和 m 个不同的质数 p1,p2,…,pm。

请你求出 1∼n 中能被 p1,p2,…,pm 中的至少一个数整除的整数有多少个。

输入格式
第一行包含整数 n 和 m。

第二行包含 m 个质数。

输出格式
输出一个整数,表示满足条件的整数的个数。

数据范围
1≤m≤16,
1≤n,pi≤109
输入样例:
10 2
2 3
输出样例:
7

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;

int n,m;
int a[20];
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for (int i=0;i<m;i++) cin>>a[i];
    int res = 0;
    for (int i=1;i<1<<m;i++)
    {
        int t=1,s=0;
        for (int j=0;j<m;j++)
        {
            if (i>>j&1) {
                if ((LL)t*a[j]>n)
                {
                    t = -1;
                    break;
                }
                t*=a[j];
                s++;
            }
        }
        if (t!=-1)
        {
            if (s%2) res+=n/t;
            else
            res-=n/t;
        }
    }
    printf("%d",res);
    return 0;
}
### 利用容斥原理求解质数 #### 容斥原理简介 容斥原理是一种用于解决集合交并运算的学方法。它可以通过加减不同子集的量来计算复杂条件下的总[^1]。 当应用于质数时,容斥原理的核心思想是排除那些能够被某些特定合整除字,从而得到剩余的质数量。具体来说,在给定范围 `[1, N]` 中,我们需要统计无法被任何小于等于 `sqrt(N)` 的质数整除目。 --- #### 使用容斥原理求解质数的具体过程 假设我们要在区间 `[1, N]` 内找到所有质数1. **定义事件** 设 \( P_i \) 表示某个整数能被第 \( i \) 个小于等于 \( \sqrt{N} \)质数 \( p_i \) 整除的概率,则整个区间的总可以表示为这些概率的组合形式[^3]。 2. **构建公式** 假设已知的小于等于 \( \sqrt{N} \)质数有 \( k \) 个,分别为 \( p_1, p_2, ..., p_k \),则满足以下公式的值即为目标范围内不可被上述任意质数整除质数量: \[ S = N - |A_{p_1}| - |A_{p_2}| - ... - |A_{p_k}| + |A_{p_1} \cap A_{p_2}| + ... \] 这里的每一项都可以通过简单的算术操作得出,例如对于单个质数 \( p_i \),\( |A_{p_i}| = \lfloor N / p_i \rfloor \)[^2]。 3. **实现细节** 可以采用递归或者迭代的方式枚举所有可能的质数组合,并依据它们的乘积对最终结果施加重叠部分的补偿(加法或减法)。以下是基于 C 语言的一个简单实现例子: ```c #include <stdio.h> #include <stdlib.h> long long count_primes(int n, int primes[], int size) { long long res = 0; for (int mask = 1; mask < (1 << size); ++mask) { // 遍历所有可能的质数组合 int cnt = __builtin_popcount(mask); long long prod = 1; for (int j = 0; j < size; ++j) { if ((mask >> j) & 1) { if (__INT64_C(1e18) / prod < primes[j]) break; // 防止溢出 prod *= primes[j]; } } if (prod > n) continue; if (cnt % 2 == 1) res -= n / prod; // 如果当前组合中有奇质数,做减法 else res += n / prod; // 否则做加法 } return n - res; // 总共n个中去掉可被至少一个小于根号n的质数整除的部分 } void sieve(int limit, int *primes, int *size) { char is_prime[limit + 1]; memset(is_prime, 1, sizeof(is_prime)); is_prime[0] = is_prime[1] = 0; for (int i = 2; i <= limit; ++i) { if (!is_prime[i]) continue; (*primes)[(*size)++] = i; for (int j = i * 2; j <= limit; j += i) is_prime[j] = 0; } } int main() { int N = 1000000; int max_p = sqrt(N); int primes[max_p], size = 0; sieve(max_p, primes, &size); printf("%lld\n", count_primes(N, primes, size)); // 输出结果 } ``` 此程序首先筛选出不超过 \( \sqrt{N} \) 的所有质数,接着运用容斥原理由低到高逐层累加/减少符合条件的倍贡献[^2]。 --- #### 注意事项 - 上述代码中的 `__builtin_popcount()` 函用来快速判断二进制掩码中设置位的量。 - 对于非常大的输入(\( N > 10^{9} \)) ,应考虑优化存储结构以及避免不必要的重复计算。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值