容斥原理及其应用

文章介绍了容斥原理在计算被不同条件整除的瓷砖数量以及在涂色游戏中获取巧克力最大数量的应用,同时提及了如何使用位运算高效枚举子集来解决问题。
摘要由CSDN通过智能技术生成

容斥原理题解

简介

  1. 如果被计数的事物有A、B两类,则A类和B类元素的总和=A类元素个数+B类元素个数—既是A类又是B类的元素个数。


    ∣ A ∪ B ∣ = ∣ A ∣ + ∣ B ∣ − ∣ A ∩ B ∣ |A \cup B| = |A| + |B| - |A \cap B| AB=A+BAB

  2. 如果被计数的事物有A、B、C三类,那么,A类和B类和C类元素个数总和= A类元素个数+ B类元素个数+C类元素个数—既是A类又是B类的元素个数—既是A类又是C类的元素个数—既是B类又是C类的元素个数+既是A类又是B类而且是C类的元素个数。

​ 即
∣ A ∪ B ∪ C ∣ = ∣ A ∣ + ∣ B ∣ + ∣ C ∣ − ∣ A ∩ B ∣ − ∣ A ∩ C ∣ − ∣ B ∩ C ∣ + ∣ A ∩ B ∩ C ∣ |A \cup B \cup C| = |A| + |B| + |C| - |A \cap B| - |A \cap C| - |B \cap C| + |A \cap B \cap C| ABC=A+B+CABACBC+ABC

  1. 以此类推,当计数的事物为A、B、C、D四类,那么
    ∣ A ∪ B ∪ C ∪ D ∣ = ∣ A ∣ + ∣ B ∣ + ∣ C ∣ + ∣ D ∣ − ∣ A ∩ B ∣ − ∣ A ∩ C ∣ − ∣ A ∩ D ∣ − ∣ B ∩ C ∣ − ∣ B ∩ D ∣ − ∣ C ∩ D ∣ + ∣ A ∩ B ∩ C ∣ + ∣ A ∩ B ∩ D ∣ + ∣ A ∩ C ∩ D ∣ + ∣ B ∩ C ∩ D ∣ − ∣ A ∩ B ∩ C ∩ D ∣ |A \cup B \cup C \cup D| = |A| + |B| + |C| + |D| - |A \cap B| - |A \cap C| - |A \cap D| - |B \cap C| - |B \cap D| - |C \cap D| + |A \cap B \cap C| + |A \cap B \cap D| + |A \cap C \cap D| + |B \cap C \cap D| - |A \cap B \cap C \cap D| ABCD=A+B+C+DABACADBCBDCD+ABC+ABD+ACD+BCDABCD

  2. 可看出,公式的符号为 + - + - + - … 即

    总数 = 所有选一个加起来的 - 所有两个圆交的情况 + 所有三个圆交的情况 - 所有四个圆交的情况……

    所以每次选奇数个,前面的符号都是正号;选偶数个,前面的符号都是负号。即下面的公式:
    ∣ ⋃ i = 1 n A i ∣ = ∑ i = 1 n ∣ A i ∣ − ∑ 1 ≤ i < j ≤ n ∣ A i ∩ A j ∣ + ∑ 1 ≤ i < j < k ≤ n ∣ A i ∩ A j ∩ A k ∣ − ⋯ + ( − 1 ) n − 1 ∣ ⋂ i = 1 n A i ∣ \left| \bigcup_{i=1}^{n} A_i \right| = \sum_{i=1}^{n} |A_i| - \sum_{1 \leq i < j \leq n} |A_i \cap A_j| + \sum_{1 \leq i < j < k \leq n} |A_i \cap A_j \cap A_k| - \cdots + (-1)^{n-1} \left| \bigcap_{i=1}^{n} A_i \right| i=1nAi=i=1nAi1i<jnAiAj+1i<j<knAiAjAk+(1)n1i=1nAi

  3. 容斥原理的证明略去。

题目描述 (涂色游戏)

给你一行n个瓷砖(初始都未被涂色),下标从 1到n,你不得不把它们画成一 种奇怪的图案。

一块未被涂色的瓷砖可以被涂成红色如果它的下标可以被a整除,一块未被涂色的瓷砖可以被涂成蓝色如果它的下标可以被b整除。

因此,如果一块瓷砖的下标即可以被a整徐又可以被b整除,则它可以被涂成红色或蓝色。

在涂色完之后,你将为每一块涂成红色的瓷砖获得p块巧克力,为每块涂成蓝色的瓷砖获得q块巧克力。

注意,你可以按照自己想要的任何顺序去涂瓷砖。 找到你能获得的巧克力的最大数量。

输入格式

一行包括5个整数n, a, b, p, q(1 <= n , a, b, p, q <= 10^9)

输出格式

打印唯一的整数s,表示你能获得的巧克力的最大数量。

样例数据

intput1:

5 2 3 12 15

output1:

39

intput2:

20 2 3 3 5

output2:

51

code:

#include <stdio.h>
// 求最大公约数
int gcd(int a, int b) {
    while (b != 0) {
        int temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}
// 求最小公倍数
int lcm(int a, int b) {
    return a * b / gcd(a, b);
}
// 计算被整除的数量
int count_divisible(int n, int x) {
    return n / x;
}
// 计算可以获得的最大巧克力数量
int max_chocolates(int n, int a, int b, int p, int q) {
    // 计算可以被a整除的瓷砖数量
    int total_red = count_divisible(n, a);
    // 计算可以被b整除的瓷砖数量
    int total_blue = count_divisible(n, b);
    // 计算同时可以被a和b整除的瓷砖数量
    int total_common = count_divisible(n, lcm(a, b));
	// 根据容斥原理计算涂成红色的瓷砖数量
    int red = total_red - total_common;
    // 根据容斥原理计算涂成蓝色的瓷砖数量
    int blue = total_blue - total_common;
    // 同时满足涂成红色和蓝色的瓷砖数量
    int common = total_common;
	// 返回总的巧克力数量
    return red * p + blue * q + common * (p > q ? p : q);
}
int main() {
    int n, a, b, p, q;
    // 读取输入
    scanf("%d %d %d %d %d", &n, &a, &b, &p, &q);
    // 计算结果并输出
    int result = max_chocolates(n, a, b, p, q);
    printf("%d\n", result);
	return 0;
}

0f511ab7b6a34f5aade4eb570524ac98

890. 能被整除的数 - AcWing题库

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

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

输入格式

第一行包含整数 n和 m。

第二行包含 m个质数。

输出格式

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

数据范围

1≤m≤16,
1≤n,pi≤10^9。

输入样例:
10 2
2 3
输出样例:
7
code:
#include <stdio.h>
#define N 20
int n, m;
int p[N];
int main()
{
    scanf("%d %d", &n, &m);
    for(int i = 0; i < m; i++){
        scanf("%d", &p[i]);
    }
    int res = 0;
    for(int i = 1; i < 1 << m; i++){
    	int t = 1, cnt = 0;
    	for(int j = 0; j < m; j++){
    		if(i >> j & 1 == 1){
    			cnt++;
    			if((long long)t * p[j] > n){
    				t = -1;
    				break;
				}
				t *= p[j];
			}
		}
		if(t != -1){
			if(cnt % 2){
				res += n / t;
			}else{
				res -= n / t;
			}
		}
	}
	printf("%d", res);
    return 0;
}
  1. 分析样例,我们发现结果是1—10中能被2整除的数量+能被3整除的数量—能被2,3整除的数量。

    image-20240429174307453

  2. 考虑更加一般的情况是,枚举出所有情况的方法在下方。

    image-20240429174629791

枚举方式:
 for(int i = 1; i < 1 << m; i++){
    	int t = 1, cnt = 0;
    	for(int j = 0; j < m; j++){
    		if(i >> j & 1 == 1){
    			cnt++;
    			if((long long)t * p[j] > n){
    				t = -1;
    				break;
				}
				t *= p[j];
			}
		}
		if(t != -1){
			if(cnt % 2){
				res += n / t;
			}else{
				res -= n / t;
			}
		}
	}

使用位运算来枚举子集是一种基于二进制数特性的方法。其原理和步骤为:

将i看成一个m位的二进制数,其中每一位上的数字如果为1表示选择,如果为0表示不选择。从1到2^m-1对应的二进制数可以枚举完所有情况。

  1. 二进制数的特性:
    • 每一位二进制数只有两种状态:0或1。这可以直接应用于集合的子集枚举问题,其中每一位代表集合中的一个元素,位的状态(0或1)决定该元素是否被选入子集中。
  2. 二进制表示的映射:
    • 一个m位的二进制数可以表示从0到 2^m-1 的所有整数。对于集合中的元素,每个数字的每一位都对应一个元素。例如,对于三元素集合 {𝑎,𝑏,𝑐},二进制数5表示为101,意味着选择了集合中的元素 𝑎 和 𝑐。
  3. 枚举过程:
    • 通过遍历整数从1到 2^m-1,可以枚举所有非空子集。通过枚举它的每一位,如果为1表示选择,如果为0表示不选。每个整数的二进制表示直接显示了哪些元素包含在当前枚举的子集中。
    • 枚举从二进制的 000...001(只选了最后一个元素)到 111...111(选了所有元素)。这样的枚举方法确保从最小的非空子集到最大的子集都被完全覆盖。
  4. 位运算的应用:
    • 使用位运算,如右移(>>)和与操作(&),来检查每一位。操作 i >> j & 1 将整数 i 右移 j 位,将检查位移到最低位,然后与1进行AND操作,这个结果直接指示第 j 位是否为1,即集合中的第 j 个元素是否被选中。
  • 15
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值