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;
}