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