容斥原理的并

文章目录

  • 简介
  • AcWing 890. 能被整除的数
    • 思路解析
    • CODE



简介

推荐题解:https://www.acwing.com/solution/content/126553/
画了图,清晰易懂,懒得打字了。
总之就是以下公式: S = S 1 + S 2 + S 3 − S 1 ∩ S 2 − S 1 ∩ S 3 − S 2 ∩ S 3 + S 1 ∩ S 2 ∩ S 3 \begin{align*} S = & S_1 + S_2 + S_3 \\ & - S_1 \cap S_2 - S_1 \cap S_3 - S_2 \cap S_3 \\ & + S_1 \cap S_2 \cap S_3 \end{align*} S=S1+S2+S3S1S2S1S3S2S3+S1S2S3
我们可以把这个式子推导到 n n n 维,奇加偶减。


AcWing 890. 能被整除的数

题目链接:https://www.acwing.com/activity/content/problem/content/960/
容斥原理的并_第1张图片

思路解析

筛出一个数的倍数,两个数的倍数 … n n n 个数的倍数,这就抽象成了 n n n 个集合的问题了。

那么如何表示选取哪几个集合(质数)呢?

  • 1 1 1 开始枚举到 n n n,将每个数看成二进制形式,如果说第 k k k 位是 1 1 1,那么就代表选第 k k k 个集合,反之不选。

如何知道被整除的数的个数?公式: n / p n / p n/p 下取整即可。

最后判断是奇数个还是偶数个,这个判断利用了二进制的一个性质:除了 2 0 2^0 20,其他所有位的和都是 2 2 2 的整数倍,所以说看是否为奇数就看它二进制最后一位是否为 1 1 1


CODE

#include 
#include 
#include 

using namespace std;

typedef long long ll;  // 定义长整型别名为ll

const int N = 20;  // 定义常量N为20
int n, m;  // 定义整型变量n和m
int p[N];  // 定义整型数组p,大小为N

int main(){
    scanf("%d%d", &n, &m);  	// 从输入中读取两个整数n和m
    
    for(int i = 0; i < m; ++i) scanf("%d", &p[i]);  // 从输入中读取m个整数到数组p中
    
    int res = 0;  	// 初始化结果为0
    for(int i = 1; i < (1 << m); ++i){  // 遍历所有的子集
        int s = 0, t = 1;  	// 初始化s和t
        
        for(int j = 0; j < m; ++j){  	// 遍历每一位
            if(i >> j & 1){  	// 如果第j位为1
                if((ll)t * p[j] > n){  	// 如果t乘以p[j]大于n
                    t = -1;  	// 将t设置为-1
                    break;  	// 跳出循环
                }
                t *= p[j];  // 更新t
                s++;  // 增加s
            }
        }
        
        if(t == -1) continue;  	// 如果t为-1,跳过当前循环
        
        if(s & 1) res += n / t;  // 如果s为奇数,增加n/t到res
        else res -= n / t;  	// 否则,从res中减去n/t
    }
    
    cout << res << endl;  // 输出结果
}

你可能感兴趣的:(算法学习记录,笔记,算法,c++)