从3个元素到n个元素:容斥原理的直观理解与公式推导

容斥原理(Inclusion-Exclusion Principle)是组合数学中一个非常重要的工具,用于计算多个集合的并集大小。它的核心思想是通过“加加减减”的方式,避免重复计数,从而准确地求出多个集合的并集大小。本文将从3个元素的容斥原理出发,逐步推导出n个元素的容斥原理公式,并给出直观的解释。


1. 从3个元素开始

假设我们有3个集合 A A A, B B B, C C C,我们希望计算它们的并集大小 ∣ A ∪ B ∪ C ∣ |A \cup B \cup C| ABC

直观理解

  1. 首先,我们简单地将每个集合的大小相加:
    ∣ A ∣ + ∣ B ∣ + ∣ C ∣ . |A| + |B| + |C|. A+B+C∣.
    但是,这样会重复计算 A ∩ B A \cap B AB, A ∩ C A \cap C AC, B ∩ C B \cap C BC 的部分。

  2. 为了修正重复计算的部分,我们减去两两交集的大小:
    ∣ A ∣ + ∣ B ∣ + ∣ C ∣ − ∣ A ∩ B ∣ − ∣ A ∩ C ∣ − ∣ B ∩ C ∣ . |A| + |B| + |C| - |A \cap B| - |A \cap C| - |B \cap C|. A+B+CABACBC∣.
    但是,这样又会过度减去 A ∩ B ∩ C A \cap B \cap C ABC 的部分。

  3. 最后,我们需要将 A ∩ B ∩ C A \cap B \cap C ABC 的部分加回来:
    ∣ A ∣ + ∣ B ∣ + ∣ C ∣ − ∣ A ∩ B ∣ − ∣ A ∩ C ∣ − ∣ B ∩ C ∣ + ∣ A ∩ B ∩ C ∣ . |A| + |B| + |C| - |A \cap B| - |A \cap C| - |B \cap C| + |A \cap B \cap C|. A+B+CABACBC+ABC∣.

公式总结

3个集合的容斥原理公式为:
∣ 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∣.


2. 推广到n个集合

现在,我们希望将容斥原理推广到n个集合 A 1 , A 2 , … , A n A_1, A_2, \dots, A_n A1,A2,,An,计算它们的并集大小 ∣ A 1 ∪ A 2 ∪ ⋯ ∪ A n ∣ |A_1 \cup A_2 \cup \dots \cup A_n| A1A2An

直观理解

  1. 第一步:加上所有单个集合的大小
    ∑ i = 1 n ∣ A i ∣ . \sum_{i=1}^n |A_i|. i=1nAi∣.
    这样会重复计算两两交集的部分。

  2. 第二步:减去所有两两交集的大小
    − ∑ 1 ≤ i < j ≤ n ∣ A i ∩ A j ∣ . -\sum_{1 \leq i < j \leq n} |A_i \cap A_j|. 1i<jnAiAj∣.
    这样会过度减去三个集合交集的部分。

  3. 第三步:加上所有三个集合交集的大小
    + ∑ 1 ≤ i < j < k ≤ n ∣ A i ∩ A j ∩ A k ∣ . +\sum_{1 \leq i < j < k \leq n} |A_i \cap A_j \cap A_k|. +1i<j<knAiAjAk∣.
    这样又会重复计算四个集合交集的部分。

  4. 依此类推

    • 对于奇数个集合的交集,我们加上它们的大小。
    • 对于偶数个集合的交集,我们减去它们的大小。

公式总结

n个集合的容斥原理公式为:
∣ A 1 ∪ A 2 ∪ ⋯ ∪ A n ∣ = ∑ 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 ∣ A 1 ∩ A 2 ∩ ⋯ ∩ A n ∣ . |A_1 \cup A_2 \cup \dots \cup A_n| = \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| - \dots + (-1)^{n+1} |A_1 \cap A_2 \cap \dots \cap A_n|. A1A2An=i=1nAi1i<jnAiAj+1i<j<knAiAjAk+(1)n+1A1A2An∣.

用求和符号表示为:
∣ A 1 ∪ A 2 ∪ ⋯ ∪ A n ∣ = ∑ k = 1 n ( − 1 ) k + 1 ∑ 1 ≤ i 1 < i 2 < ⋯ < i k ≤ n ∣ A i 1 ∩ A i 2 ∩ ⋯ ∩ A i k ∣ . |A_1 \cup A_2 \cup \dots \cup A_n| = \sum_{k=1}^n (-1)^{k+1} \sum_{1 \leq i_1 < i_2 < \dots < i_k \leq n} |A_{i_1} \cap A_{i_2} \cap \dots \cap A_{i_k}|. A1A2An=k=1n(1)k+11i1<i2<<iknAi1Ai2Aik∣.


3. 公式推导

我们可以通过数学归纳法证明n个集合的容斥原理公式。

归纳基础

  • n = 1 n = 1 n=1 时,公式显然成立:
    ∣ A 1 ∣ = ∣ A 1 ∣ . |A_1| = |A_1|. A1=A1∣.
  • n = 2 n = 2 n=2 时,公式为:
    ∣ A 1 ∪ A 2 ∣ = ∣ A 1 ∣ + ∣ A 2 ∣ − ∣ A 1 ∩ A 2 ∣ . |A_1 \cup A_2| = |A_1| + |A_2| - |A_1 \cap A_2|. A1A2=A1+A2A1A2∣. 这也是容斥原理的基本形式。

归纳假设

假设对于 n = m n = m n=m,容斥原理公式成立,即:
∣ A 1 ∪ A 2 ∪ ⋯ ∪ A m ∣ = ∑ k = 1 m ( − 1 ) k + 1 ∑ 1 ≤ i 1 < i 2 < ⋯ < i k ≤ m ∣ A i 1 ∩ A i 2 ∩ ⋯ ∩ A i k ∣ . |A_1 \cup A_2 \cup \dots \cup A_m| = \sum_{k=1}^m (-1)^{k+1} \sum_{1 \leq i_1 < i_2 < \dots < i_k \leq m} |A_{i_1} \cap A_{i_2} \cap \dots \cap A_{i_k}|. A1A2Am=k=1m(1)k+11i1<i2<<ikmAi1Ai2Aik∣.

归纳步骤

对于 n = m + 1 n = m + 1 n=m+1,我们需要证明:
∣ A 1 ∪ A 2 ∪ ⋯ ∪ A m + 1 ∣ = ∣ A 1 ∪ A 2 ∪ ⋯ ∪ A m ∣ + ∣ A m + 1 ∣ − ∣ ( A 1 ∪ A 2 ∪ ⋯ ∪ A m ) ∩ A m + 1 ∣ . |A_1 \cup A_2 \cup \dots \cup A_{m+1}| = |A_1 \cup A_2 \cup \dots \cup A_m| + |A_{m+1}| - |(A_1 \cup A_2 \cup \dots \cup A_m) \cap A_{m+1}|. A1A2Am+1=A1A2Am+Am+1(A1A2Am)Am+1∣.
根据归纳假设,将 ∣ A 1 ∪ A 2 ∪ ⋯ ∪ A m ∣ |A_1 \cup A_2 \cup \dots \cup A_m| A1A2Am ∣ ( A 1 ∪ A 2 ∪ ⋯ ∪ A m ) ∩ A m + 1 ∣ |(A_1 \cup A_2 \cup \dots \cup A_m) \cap A_{m+1}| (A1A2Am)Am+1 展开,即可得到 n = m + 1 n = m + 1 n=m+1 的容斥原理公式。


4. 应用示例

假设有4个集合 A 1 , A 2 , A 3 , A 4 A_1, A_2, A_3, A_4 A1,A2,A3,A4,它们的并集大小为:
∣ A 1 ∪ A 2 ∪ A 3 ∪ A 4 ∣ = ∣ A 1 ∣ + ∣ A 2 ∣ + ∣ A 3 ∣ + ∣ A 4 ∣ − ∣ A 1 ∩ A 2 ∣ − ∣ A 1 ∩ A 3 ∣ − ∣ A 1 ∩ A 4 ∣ − ∣ A 2 ∩ A 3 ∣ − ∣ A 2 ∩ A 4 ∣ − ∣ A 3 ∩ A 4 ∣ + ∣ A 1 ∩ A 2 ∩ A 3 ∣ + ∣ A 1 ∩ A 2 ∩ A 4 ∣ + ∣ A 1 ∩ A 3 ∩ A 4 ∣ + ∣ A 2 ∩ A 3 ∩ A 4 ∣ − ∣ A 1 ∩ A 2 ∩ A 3 ∩ A 4 ∣ . |A_1 \cup A_2 \cup A_3 \cup A_4| = |A_1| + |A_2| + |A_3| + |A_4| - |A_1 \cap A_2| - |A_1 \cap A_3| - |A_1 \cap A_4| - |A_2 \cap A_3| - |A_2 \cap A_4| - |A_3 \cap A_4| + |A_1 \cap A_2 \cap A_3| + |A_1 \cap A_2 \cap A_4| + |A_1 \cap A_3 \cap A_4| + |A_2 \cap A_3 \cap A_4| - |A_1 \cap A_2 \cap A_3 \cap A_4|. A1A2A3A4=A1+A2+A3+A4A1A2A1A3A1A4A2A3A2A4A3A4+A1A2A3+A1A2A4+A1A3A4+A2A3A4A1A2A3A4∣.


5. 总结

容斥原理通过交替加减集合交集的大小,避免了重复计数,从而准确地计算多个集合的并集大小。从3个集合的简单情况出发,我们可以推广到n个集合的一般情况,并通过数学归纳法严格证明其正确性。容斥原理在概率论、组合数学、计算机科学等领域有着广泛的应用,是解决复杂计数问题的有力工具。

6.练习

ACWing 能被整除的数

问题描述

给定一个整数 n n n m m m 个不同的质数 p 1 , p 2 , … , p m p_1, p_2, \dots, p_m p1,p2,,pm,要求求出 1 ∼ n 1 \sim n 1n 中能被至少一个质数整除的整数有多少个。


解题思路

这个问题可以通过容斥原理来解决。容斥原理的核心思想是通过“加加减减”的方式,避免重复计数,从而准确地计算多个集合的并集大小。

1. 问题转化
  • A i A_i Ai 表示 1 ∼ n 1 \sim n 1n 中能被 p i p_i pi 整除的整数的集合。
  • 我们需要计算的是:
    ∣ A 1 ∪ A 2 ∪ ⋯ ∪ A m ∣ , |A_1 \cup A_2 \cup \dots \cup A_m|, A1A2Am,
    1 ∼ n 1 \sim n 1n 中能被至少一个质数整除的整数的个数。
2. 容斥原理公式

根据容斥原理,多个集合的并集大小可以通过以下公式计算:
∣ A 1 ∪ A 2 ∪ ⋯ ∪ A m ∣ = ∑ i = 1 m ∣ A i ∣ − ∑ 1 ≤ i < j ≤ m ∣ A i ∩ A j ∣ + ∑ 1 ≤ i < j < k ≤ m ∣ A i ∩ A j ∩ A k ∣ − ⋯ + ( − 1 ) m + 1 ∣ A 1 ∩ A 2 ∩ ⋯ ∩ A m ∣ . |A_1 \cup A_2 \cup \dots \cup A_m| = \sum_{i=1}^m |A_i| - \sum_{1 \leq i < j \leq m} |A_i \cap A_j| + \sum_{1 \leq i < j < k \leq m} |A_i \cap A_j \cap A_k| - \dots + (-1)^{m+1} |A_1 \cap A_2 \cap \dots \cap A_m|. A1A2Am=i=1mAi1i<jmAiAj+1i<j<kmAiAjAk+(1)m+1A1A2Am∣.

3. 计算交集大小
  • 对于任意 k k k 个质数 p i 1 , p i 2 , … , p i k p_{i_1}, p_{i_2}, \dots, p_{i_k} pi1,pi2,,pik,它们的交集大小为:
    ∣ A i 1 ∩ A i 2 ∩ ⋯ ∩ A i k ∣ = ⌊ n lcm ( p i 1 , p i 2 , … , p i k ) ⌋ . |A_{i_1} \cap A_{i_2} \cap \dots \cap A_{i_k}| = \left\lfloor \frac{n}{\text{lcm}(p_{i_1}, p_{i_2}, \dots, p_{i_k})} \right\rfloor. Ai1Ai2Aik=lcm(pi1,pi2,,pik)n.由于 p i p_i pi 是质数,且互不相同,因此:
    lcm ( p i 1 , p i 2 , … , p i k ) = p i 1 ⋅ p i 2 ⋅ ⋯ ⋅ p i k . \text{lcm}(p_{i_1}, p_{i_2}, \dots, p_{i_k}) = p_{i_1} \cdot p_{i_2} \cdot \dots \cdot p_{i_k}. lcm(pi1,pi2,,pik)=pi1pi2pik.
4. 二进制枚举

为了高效地计算所有可能的交集,我们可以使用二进制枚举的方法:

  • 对于 m m m 个质数,共有 2 m − 1 2^m - 1 2m1 种非空子集。
  • 每个子集对应一个交集,其符号由子集的大小决定:
    • 如果子集大小为奇数,则符号为正。
    • 如果子集大小为偶数,则符号为负。

代码实现

以下是基于容斥原理和二进制枚举的 Python 与 c++ 实现:

def count_divisible_numbers(n, primes):
    m = len(primes)
    total = 0

    # 遍历所有非空子集
    for mask in range(1, 1 << m):
        bits = bin(mask).count('1')  # 子集大小
        lcm = 1
        for i in range(m):
            if mask & (1 << i):  # 第 i 个质数在子集中
                lcm *= primes[i]
        count = n // lcm  # 交集大小
        if bits % 2 == 1:  # 奇数子集,符号为正
            total += count
        else:  # 偶数子集,符号为负
            total -= count

    return total
#include <iostream>
using namespace std;

// 主函数
int main() {
    // 提高I/O效率
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    int n, m;  // n为上限值,m为数组p的长度
    cin >> n >> m;

    int primes[30];  // 存储输入的质数
    for (int i = 0; i < m; i++) {
        cin >> primes[i];
    }

    int result = 0;  // 最终结果

    // 遍历所有可能的子集(使用位掩码)
    for (int subsetMask = 1; subsetMask <= (1 << m) - 1; subsetMask++) {
        int countSelectedPrimes = 0;  // 当前子集中选择的质数个数
        long long productOfPrimes = 1;  // 当前子集中质数的乘积
        bool isProductValid = true;  // 标记当前子集的乘积是否有效

        // 检查每个质数是否在当前子集中
        for (int j = 0; j < m; j++) {
            if ((subsetMask >> j) & 1) {  // 如果第j位被选中
                // 检查乘积是否会超过n
                if ((productOfPrimes * primes[j]) > n) {
                    isProductValid = false;
                    break;
                }
                productOfPrimes *= primes[j];  // 更新乘积
                countSelectedPrimes++;  // 增加计数
            }
        }

        // 如果乘积无效,跳过该子集
        if (!isProductValid) continue;

        // 根据选择的质数个数决定是加还是减
        if (countSelectedPrimes % 2 == 1) {
            result += n / productOfPrimes;
        } else {
            result -= n / productOfPrimes;
        }
    }

    // 输出最终结果
    cout << result << endl;
    return 0;
}

示例

输入:
n = 20
primes = [2, 3, 5]
计算过程:
  1. 单个质数的交集:

    • ∣ A 1 ∣ = ⌊ 20 / 2 ⌋ = 10 |A_1| = \lfloor 20 / 2 \rfloor = 10 A1=20/2=10
    • ∣ A 2 ∣ = ⌊ 20 / 3 ⌋ = 6 |A_2| = \lfloor 20 / 3 \rfloor = 6 A2=20/3=6
    • ∣ A 3 ∣ = ⌊ 20 / 5 ⌋ = 4 |A_3| = \lfloor 20 / 5 \rfloor = 4 A3=20/5=4
  2. 两两质数的交集:

    • ∣ A 1 ∩ A 2 ∣ = ⌊ 20 / 6 ⌋ = 3 |A_1 \cap A_2| = \lfloor 20 / 6 \rfloor = 3 A1A2=20/6=3
    • ∣ A 1 ∩ A 3 ∣ = ⌊ 20 / 10 ⌋ = 2 |A_1 \cap A_3| = \lfloor 20 / 10 \rfloor = 2 A1A3=20/10=2
    • ∣ A 2 ∩ A 3 ∣ = ⌊ 20 / 15 ⌋ = 1 |A_2 \cap A_3| = \lfloor 20 / 15 \rfloor = 1 A2A3=20/15=1
  3. 三个质数的交集:

    • ∣ A 1 ∩ A 2 ∩ A 3 ∣ = ⌊ 20 / 30 ⌋ = 0 |A_1 \cap A_2 \cap A_3| = \lfloor 20 / 30 \rfloor = 0 A1A2A3=20/30=0
  4. 应用容斥原理:
    ∣ A 1 ∪ A 2 ∪ A 3 ∣ = 10 + 6 + 4 − 3 − 2 − 1 + 0 = 14. |A_1 \cup A_2 \cup A_3| = 10 + 6 + 4 - 3 - 2 - 1 + 0 = 14. A1A2A3=10+6+4321+0=14.

输出:
14

复杂度分析

  1. 时间复杂度

    • 二进制枚举的子集总数为 2 m − 1 2^m - 1 2m1
    • 对于每个子集,计算交集大小的时间复杂度为 O ( m ) O(m) O(m)
    • 总时间复杂度为 O ( m ⋅ 2 m ) O(m \cdot 2^m) O(m2m)
  2. 空间复杂度

    • 只需要常数空间存储中间结果,空间复杂度为 O ( 1 ) O(1) O(1)

总结

通过容斥原理和二进制枚举,我们可以高效地计算 1 ∼ n 1 \sim n 1n 中能被至少一个质数整除的整数的个数。这种方法不仅适用于质数,还可以推广到任意一组数的倍数计数问题。容斥原理是解决复杂计数问题的强大工具,而二进制枚举则提供了一种高效的实现方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值