加法原理的延申
韦恩图,奇加偶减

并转交

许多问题都有“交比并简单的性质”

例题

890. 能被整除的数

给定一个整数 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

思路
利用容斥原理
1-n中能被pi整除的数有 n / pi
这其中可能存在数val,既能被pi整除,又能被pj整除,被加了两次,所有要减去 **n / pi * pj**

以此类推

问题来了,如何能遍历所有p的乘积?
p1, p2, … pk
p1p2, p1p3, … pk-1pk

p1p2p3…pk

利用二进制位0和1表示每一个pi存在与否,这样m个质数就能用m个二进制位表示有无了!
例如 m = 15 直接用i从1开始,遍历到15,每次加一即可。

  1. import java.util.*;
  2. public class Main {
  3. public static void main(String[] args) {
  4. Scanner sc = new Scanner(System.in);
  5. int n = sc.nextInt(), m = sc.nextInt();
  6. int[] p = new int[m];
  7. for (int i = 0; i < m; i++) {
  8. p[i] = sc.nextInt();
  9. }
  10. int res = 0;
  11. //注意i从1开始
  12. for (int i = 1; i < (1 << m); i++) {
  13. int t = 1, cnt = 0;
  14. for (int j = 0; j < m; j++) {
  15. if ((i >> j & 1) == 1) {
  16. cnt++;
  17. //出界,集合中已经没有该数字了
  18. if ((long) t * p[j] > n) {
  19. t = -1;
  20. break;
  21. }
  22. t *= p[j];
  23. }
  24. }
  25. if (t != -1) {
  26. if ((cnt & 1) == 1) res += n / t;
  27. else res -= n / t;
  28. }
  29. }
  30. System.out.println(res);
  31. }
  32. }

例1:用容斥原理求解错位排列的数量
例如,长度为4的错位排列的数量。
容斥原理 - 图1

例2:求容斥原理 - 图2
容斥原理 - 图3,其中容斥原理 - 图4指莫比乌斯函数