容斥原理
如上图所示:
∣ s 1 ∪ s 2 ∪ s 3 ∣ = ∣ s 1 ∣ + ∣ s 2 ∣ + ∣ s 3 ∣ − ∣ s 1 ∩ s 2 ∣ − ∣ s 1 ∩ s 3 ∣ − ∣ s 2 ∩ s 3 ∣ + ∣ s 1 ∩ s 2 ∩ s 3 ∣ \begin{aligned} |s_1 \cup s_2 \cup s_3|&=|s_1|+|s_2|+|s_3|-|s_1 \cap s_2|-|s_1 \cap s_3|-|s_2 \cap s_3|+|s_1 \cap s_2 \cap s_3| \end{aligned} ∣s1∪s2∪s3∣=∣s1∣+∣s2∣+∣s3∣−∣s1∩s2∣−∣s1∩s3∣−∣s2∩s3∣+∣s1∩s2∩s3∣
时间复杂度:
C n 1 + C n 2 + C n 3 + ⋯ + C n n C_n^1+C_n^2+C_n^3+\cdots+C_n^n Cn1+Cn2+Cn3+⋯+Cnn
因为 C n 0 + C n 1 + C n 2 + C n 3 + ⋯ + C n n = 2 n C_n^0+C_n^1+C_n^2+C_n^3+\cdots+C_n^n=2^n Cn0+Cn1+Cn2+Cn3+⋯+Cnn=2n,等式的左右两边都是等于从n个中选任意多个数的方案, 2 2 2表示第 i i i个到底选不选的方案数, 2 n 2^n 2n就表示 n n n个中选不选对应数的方案数。
所以 C n 1 + C n 2 + C n 3 + ⋯ + C n n = 2 n − C n 0 = 2 n − 1 C_n^1+C_n^2+C_n^3+\cdots+C_n^n=2^n-C_n^0=2^n-1 Cn1+Cn2+Cn3+⋯+Cnn=2n−Cn0=2n−1
同理存在等式: C k 1 − C k 2 + C k 3 − C k 4 + ⋯ + ( − 1 ) k − 1 C k k = 1 C_k^1-C_k^2+C_k^3-C_k^4+\cdots+(-1)^{k-1}C_k^k=1 Ck1−Ck2+Ck3−Ck4+⋯+(−1)k−1Ckk=1
所以 ∣ s 1 ∪ s 2 ∪ s 3 ∣ = ∑ i ∣ s i ∣ − ∑ i ⋅ j ∣ s i ∩ s j ∣ + ∑ i j k ∣ s i ∩ s j ∩ s k ∣ − ⋯ |s_1\cup s_2 \cup s_3|=\sum_i|s_i|-\sum_{i \cdot j}|s_i \cap s_j|+\sum_{ijk}|s_i\cap s_j \cap s_k|-\cdots ∣s1∪s2∪s3∣=∑i∣si∣−∑i⋅j∣si∩sj∣+∑ijk∣si∩sj∩sk∣−⋯
能被整除的数
题解:
这里主要用二进制数表示某个数是否被选,1代表被选,0代表不被选,二进制中1的个数为奇数,说明为奇数个集合相交,前面的符号为+,反之为偶数个集合相交,前面的符号为-。
import java.io.*;
public class Main{
static int N=20;
static int[] p=new int[N];
public static void main(String[] args)throws IOException{
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
String[] strs=in.readLine().split(" ");
int n=Integer.parseInt(strs[0]);
int m=Integer.parseInt(strs[1]);
strs=in.readLine().split(" ");
for(int i=0;i<m;i++)p[i]=Integer.parseInt(strs[i]);
int res=0;
for(int i=1;i<(1<<m);i++){
int t=1;
int cnt=0;
for(int j=0;j<m;j++){
if((i>>j&1)==1){
cnt++;
if((long)t*p[j]>n){
t=-1;
break;
}
t=t*p[j];
}
}
if(t!=-1){
if(cnt%2==0)res-=n/t;
else res+=n/t;
}
}
System.out.println(res);
}
}