①. 题目
②. 思路
- 容斥原理公式 (奇数相加,偶数相减)
- 样例分析
n = 10, p1=2,p2=3, 求1-10中能满足能整除p1或p2的个数, 即2,3,4,6,8,9,10,共7个
- 每个集合实际上并不需要知道具体元素是什么,只要知道这个集合的大小,大小为|Si|=n/pi, 比如题目中|S1|=10/2=5,|S2|=10/3=3
- 交集的大小如何确定?因为pi均为质数,这些质数的乘积就是他们的最小公倍数,n除这个最小公倍数就是交集的大小,故|S1⋂S2|=n/(p1∗p2)=10/(2∗3)=1
- 如何用代码表示每个集合的状态?这里使用的二进制,以m = 4为例,所以需要4个二进制位来表示每一个集合选中与不选的状态,1101,这里表示选中集合S1,S2,S4,故这个集合中元素的个数为 n/(p1∗p2∗p4), 因为集合个数是3个,根据公式,前面的系数为(−1)3−1=1。所以到当前这个状态时,应该是res+=n/(p1∗p2∗p4) 。这样就可以表示的范围从0000到1111的每一个状态
③. 学习点
容斥原理
④. 代码实现
import java.util.Scanner;
public class Main {
static int N=20;
static int[] p=new int[N];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
for (int i = 0; i <m; i++) {
p[i]=sc.nextInt();
}
int res=0;
//枚举从1 到 1111...(m个1)的每一个集合状态, (至少选中一个集合)
for (int i =1; i <1<<m; i++) {
long t=1; //选中集合对应质数的乘积
long s=0; //选中的集合1的数量
//枚举当前状态的每一位
for (int j = 0; j <m; j++) {
//选中一个集合
if((i>>j&1)==1) {
//乘积大于n, 则n/t = 0, 跳出这轮循环
if(t*p[j]>n) {
t=-1;
break;
}
t*=p[j];
s++; //有一个1,集合数量+1
}
}
if(t!=-1) {
if(s%2==1) {
res+=n/t; //奇数+ n/t为当前这种状态的集合数量
}else {
res-=n/t; //偶数-
}
}
}
System.out.println(res);
}
}