HDU-1796 How many integers can you find(容斥原理)

                            How many integers can you find

                          Time Limit: 12000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
                                                  Total Submission(s): 11704    Accepted Submission(s): 3506

Problem Description

  Now you get a number N, and a M-integers set, you should find out how many integers which are small than N, that they can divided exactly by any integers in the set. For example, N=12, and M-integer set is {2,3}, so there is another set {2,3,4,6,8,9,10}, all the integers of the set can be divided exactly by 2 or 3. As a result, you just output the number 7.

Input

  There are a lot of cases. For each case, the first line contains two integers N and M. The follow line contains the M integers, and all of them are different from each other. 0<N<2^31,0<M<=10, and the M integer are non-negative and won’t exceed 20.

Output

  For each case, output the number.

Sample Input

12 2 2 3

Sample Output

7

http://acm.hdu.edu.cn/showproblem.php?pid=1796

题意:

给你n和m,m代表有m个除数。下面一行为给出的m集合。现在要你求m集合中的每一个数能被n整除的个数。

思路一:(dfs)

利用容斥定理,先找出1...m内每一个被n整除的个数,再减去1...m集合中能被两个数同时除的个数,也就是找他两个的最小公倍数。然后再加上1...m集合中能被三个数同时除的个数,然后减去1...m集合中能被四个数同时除的个数。以此类推,可以用dfs。注意m集合中不能为0。要特别判断一下。

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
long long a[30];
long long sum1;

int m,cnt,n;

long long gcd(long long p, long long b)
{
	return !b ? p : gcd( b, p%b);
}

void dfs(int i,long long lcm,int tot)
{
	lcm = a[i] / gcd(a[i], lcm) * lcm;
	
	if(tot % 2 == 0){
		sum1 -= (n-1) / lcm;
	}
	else
		sum1 += (n-1) / lcm;
	for(int j = i+1; j < cnt; j++) {
		dfs(j, lcm, tot+1);
	}
}
int main()
{
	while(cin >> n >> m) {
	
		sum1 = 0;
		cnt = 1;
		long long t; 
		for(int i = 1; i <= m; i++) {
			
			cin >> t;
			if(t != 0) {
				a[cnt++] = t;
				
			}
				
		}
		for(int i = 1; i < cnt; i++) {
			dfs(i, a[i], 1);
		}		
		cout << sum1 << endl;
			
	}
	 	
	
	return 0;
 } 

思路二(二进制枚举)

参考博客:https://blog.csdn.net/zhhx2001/article/details/51848789

 

三种常用的位运算符:与&、或|、异或^;

与运算:两者都为 1时,结果即为1,否则为0。--有0出0
或运算:两者都为 00时,结果即为0,否则为1。--有1出1
异或运算:是两者同为 0 或1 时,结果即为0,否则为1。相同出1 ,相异出0;
位运算符中有两种操作,左移<<和右移>>。
对于A << B,表示把A转化为二进制后向左移动B位(在末尾添加B个0)。

对于A >> B,表示把A转化为二进制后向右移动B位(删除末尾的B位)。

如2<<2 就是二进制的10左移2位:二进制的1000 转为10进制为8;
 

如果二进制存在两个1,也就是取两个的最小公倍数,如果存在3个1,也就是取三个的最小公倍数。

代码:

#include<bits/stdc++.h>
using namespace std;
int s[20];
int gcd(int a, int b)
{
	return b ? gcd( b, a%b) : a;
}
int main()
{
	long long n, m;
	while(cin >> n >> m){
		int Case = 1;
		while(m--) {
			int t;
			cin >> t;
			if(t != 0)
				s[Case++] = t;
		}
		Case--;
		int ans = 0; 
		for(int i = 1; i < (1 << Case); i++) {
			int k = 1; 
			int x = 0;
			for(int j = 0; j < Case; j++) {
				if(i & (1 << j)) {
					k = s[j+1] / gcd( s[j+1], k) * k;
					x++;
				}
			}
			if(x & 1) 
				ans += (n-1) / k;
			else
				ans -= (n-1) / k;
		}
		cout << ans << endl;
	}
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Sclong0218/article/details/83097072