Acwing---890. 能被整除的数 (Java)_数学知识_容斥原理

原题链接

①. 题目

在这里插入图片描述

②. 思路

在这里插入图片描述

  • 容斥原理公式 (奇数相加,偶数相减)
    在这里插入图片描述
  • 样例分析
    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);
	}

}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_45480785/article/details/114104469