容斥原理
P890 能被整除的数
容斥原理:
A
∪
B
∪
C
⋅
⋅
H
∪
I
⋅
⋅
⋅
=
A
+
B
+
C
+
⋅
⋅
⋅
⋅
⋅
−
(
A
∩
B
)
−
(
A
∩
C
)
−
⋅
⋅
⋅
⋅
+
(
A
∩
B
∩
C
)
+
(
B
∩
C
∩
D
)
−
(
A
∩
B
∩
C
∩
D
)
−
(
A
∩
C
∩
D
∩
E
)
⋅
⋅
⋅
⋅
⋅
A\cup B \cup C··H\cup I···=A+B+C+·····-(A\cap B)-(A \cap C)-····+(A\cap B\cap C)+(B\cap C\cap D)-(A\cap B\cap C\cap D)-(A\cap C\cap D\cap E)·····
A∪B∪C⋅⋅H∪I⋅⋅⋅=A+B+C+⋅⋅⋅⋅⋅−(A∩B)−(A∩C)−⋅⋅⋅⋅+(A∩B∩C)+(B∩C∩D)−(A∩B∩C∩D)−(A∩C∩D∩E)⋅⋅⋅⋅⋅
时间复杂度分析:一个集合共有
C
n
1
C_n^1
Cn1项,两个集合共有
C
n
2
C_n^2
Cn2项,以此类推,所以最后共有
C
n
1
+
C
n
2
+
⋅
⋅
⋅
⋅
+
C
n
n
C_n^1+C_n^2+····+C_n^n
Cn1+Cn2+⋅⋅⋅⋅+Cnn项,把这个式子补加一个
C
n
0
C_n^0
Cn0答案为
2
n
2^n
2n,所以这个式子最后有
2
n
−
1
2^n-1
2n−1项,时间复杂度为O(
2
n
2^n
2n-1)
证明:
思路:只需证明等式左边的每一个元素在右边都恰好被加了一次
设左边任意元素x,在k个集合中出现过,设T为x被加的次数,
则T=
C
k
1
−
C
k
2
+
C
k
3
−
C
k
4
+
⋅
⋅
⋅
⋅
+
(
−
1
)
k
−
1
⋅
C
k
k
C_k^1-C_k^2+C_k^3-C_k^4+····+(-1)^{k-1}\cdot C_k^k
Ck1−Ck2+Ck3−Ck4+⋅⋅⋅⋅+(−1)k−1⋅Ckk
通过二项式定理得
(
1
−
x
)
k
=
C
k
0
−
C
k
1
⋅
x
1
+
C
k
2
⋅
x
2
−
C
k
3
⋅
x
3
+
⋅
⋅
⋅
(
−
1
)
k
⋅
C
k
k
⋅
x
k
(1-x)^k=C_k^0-C_k^1\cdot x^1+C_k^2\cdot x^2-C_k^3\cdot x^3+···(-1)^k\cdot C_k^k\cdot x^k
(1−x)k=Ck0−Ck1⋅x1+Ck2⋅x2−Ck3⋅x3+⋅⋅⋅(−1)k⋅Ckk⋅xk,当x为1时,原式等于
C
k
0
−
T
=
1
−
T
=
0
C_k^0-T=1-T=0
Ck0−T=1−T=0,所以T=1,QED.
暴力:O(nm)会超时,只能用容斥原理
时间复杂度分析: 1~n中p的倍数的个数为
⌊
n
p
⌋
\lfloor\frac{n}{p}\rfloor
⌊pn⌋ ,集合
S
i
S_i
Si表示1~n中pi的倍数,假设算集合
S
1
∩
S
2
⋅
⋅
⋅
∩
S
m
S_1\cap S_2···\cap S_m
S1∩S2⋅⋅⋅∩Sm中的元素个数时,我们需要计算
n
p
1
⋅
p
2
⋅
⋅
⋅
p
m
\frac{n}{p_1\cdot p_2···p_m}
p1⋅p2⋅⋅⋅pmn,需要计算m次,时间复杂度为O(m),我们共有
2
m
−
1
2^m-1
2m−1个集合,所以总时间复杂度O(
2
m
∗
m
2^m*m
2m∗m)
本题也用到二进制枚举状态
#include<iostream>
#include<algorithm>
typedef long long LL;
using namespace std;
const int 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;
//一共有2^m-1项
for(int i=1;i<1<<m;i++){ //用m位二进制来表示状态
int t=1;//质数乘积
int cnt=0; //每种选法中包含几个集合
for(int j=0;j<m;j++){ //分别看每一位
if(i>>j&1){
if((LL)t*p[j]>n){
t=-1;
break;
}
t*=p[j];
cnt++;
}
}
if(t!=-1){
if(cnt%2) res+=n/t; //选中的集合数为奇数时加
else res-=n/t; //偶数时减
}
}
printf("%d",res);
return 0;
}