容斥原理+二进制枚举

容斥原理
在这里插入图片描述
n范围内不与x互质的数=Σ(n/(任意1个质因数)) - Σ(n/(任意2个质因数的乘积)) + Σ(n/(任意3个质因数的乘积)) - Σ(n/(任意4个质因数的乘积))……
简单来说就是奇加偶减

那么怎么去算呢 我们可以用二进制的每一位去算个数
比如质因数有2 3 5 那么我们可以用三位二进制来表示有没选这三个数
001 说明是2的倍数
010 说明是3的倍数
011 说明既是2的倍数又是3的倍数 也就是6的倍数
100 说明是5的倍数
101 说明是10的倍数
111 说明是15的倍数
又因为不需要去考虑000的情况 也就是空集的情况 所有一共有2^3-1种
那么怎么枚举呢 可以用位运算去判断每一位是不是1 用一个变量num记录1的个数 如果是奇数 根据容斥原理 我们就加上这个数 偶数就减去即可
并去计算n内有多少个数字是这个数的倍数
比如010 是3的倍数 如果是n=17 17/3=5 说明范围内有5个是3的倍数 又因为只有奇数(1)个质因数 所以要加上5 注意这里算的结果是与x不互质的数 最后总数减去不互质的数自然就是互质的数了

下面给出一个求n范围内与2,3,5互质的数的个数的示例

#include<bits/stdc++.h>
using namespace std;
int a[]= {2,3,5};
int main()
{
    int n;
    while(cin>>n)
    {
        int len=3,up=1<<3,sum=0;
        for(int i=1; i<up; i++)
        {
            int ans=1,num=0;
            for(int j=0; j<len; j++)
            {
                if((i>>j)&1)
                {
                    num++;
                    ans*=a[j];
                }
            }
            if(num&1)
                sum+=n/ans;
            else
                sum-=n/ans;
        }
        cout<<n-sum<<endl;

    }
}

### 利用容斥原理求解质数 #### 容斥原理简介 容斥原理是一种用于解决集合交并运算的数学方法。它可以通过加减不同子集的数量来计算复杂条件下的总数[^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
发出的红包

打赏作者

我不会c语言

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值