容斥原理
容斥原理
韦恩图,对于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\) 中的至少一个数整除的整数有多少个。
- 朴素算法 \(O(mn)\)
- 容斥原理
\[\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);
}
“风雪越是呼啸,雪莲越是绽放”