容斥原理

容斥原理

韦恩图
韦恩图,对于3个圆

\[\begin{array}{l} S_{ABC} = \\ +S_1 + S_2 + S_3\\ -S_1\cap S_2 -S_2\cap S_3 - S_3\cap S_1\\ +S_1 \cap S_2 \cap S_3 \end{array} \]

对于2个圆

\[S_1 + S_2 - S_1\cap S_2 \]

于是我们可推广对\(n\)个集合

\[\begin{array}{l} |S_1 \cup S_2 \cdots S_n|\\ =S_1 + S_2 + \cdots S_n\\ -|S_1 \cap S_2| + |S_3 \cap S_4| \cdots\\ + |S_1\cap S_2 \cap S_3| \dots\\ \cdots \end{array} \]

项数个数为

\[C_n^0 + C_n^{1} + C_n^2 + \cdots C_n^n = 2^n \]

时间复杂度为$2^n $。

例题

给定一个整数 \(n\) 和$ m$ 个不同的质数 \(p_1,p_2,…,p_m\)
请你求出 \(1∼n\)
中能被 \(p_1,p_2,…,p_m\) 中的至少一个数整除的整数有多少个。

  1. 朴素算法 \(O(mn)\)
  2. 容斥原理

\[\begin{array}{l} S_2 = \left \{ 2,4,6,8,10 \right \} \\ S_3 = \left \{ 3,6,9 \right \}\\ |S_2 \cup S_3|\\ = |S_1| + |S_2| -|S_1 \cap S_3|\\ =7 \end{array} \]

\(|S_p|\) 表示 \(1 ~ n\)\(p\) 的倍数的个数 \(\frac{n}{p}\)下取整。

\[\begin{array}{l} |S_1 \cup S_2 \cup S_2 \cup S_ 4 \cdots \cup S_k|\\ = \frac{n}{p_1 \times p_2 \times p_3 \times \cdots p_k} \end{array} \]

时间复杂度为\(O(k)\),有 \(2^m\)个集合,总复杂度为\(O(2^{m + k})\)

代码如下

#include <iostream>
#include <algorithm>

using namespace std;


const int N = 20 ;
typedef long long LL;


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)
            {
            cnt ++ ;
        
            if((LL) 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);
}

posted @ 2022-07-12 22:21  Erfu  阅读(62)  评论(0编辑  收藏  举报