题意:给出m个数,问1到n-1有多少个至少能被a中一个数整除
思路:有两种实现方式,一种是位运算,一种是dfs,先理解好位运算,再理解好这题的搜索树,dfs自然就写出来了
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1796
位运算 :
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
int n, m;
int a[30];
ll gcd(ll a, ll b)
{
return b==0 ? a : gcd(b, a%b);
}
void solve()
{
ll res = 0;
int len = 1<<m;
for(int i=1; i<len; i++)
{
int cnt = 0;
ll lcm = 1;
for(int j=0; j<m; j++)
{
if(i & (1<<j))//这里的a[j]要保证不为0
{
cnt++; //多个数的lcm可以由两个数推广
lcm = lcm / gcd(lcm, a[j])*a[j]; //因为gcd必定可以整除lcm,这样写很好地防止了溢出
if(lcm > n) break;//如果lcm>n,则n/lcm=0,不会对结果造成影响,因此在溢出之前break
}
}
if(cnt & 1) res += n/lcm;
else res -= n/lcm;
}
printf("%I64d\n", res);
}
int main()
{
while(~scanf("%d%d", &n, &m))
{
n--;
int c =0, x;
for(int i=0; i<m; i++)
{
scanf("%d", &x);
if(x)
a[c++] = x;
}
m = c;
solve();
}
return 0;
}
dfs:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
int n, m;
int a[25];
ll ans, lcm;
ll gcd(ll a, ll b)
{
return b == 0 ? a : gcd(b, a%b);
}
void dfs(int cur, ll val, int flag)
{
for(int i = cur; i <= m; i++)
{
if(a[i])
{
lcm = a[i] / gcd(val, a[i]) *val;
if(lcm > n) continue;//当lcm > n,就没有必要继续下去,因为再怎么加 n/lcm也是0,ans不会有任何变化
ans += flag * ( n / lcm );
dfs(i + 1, lcm, -flag);
}
}
}
int main()
{
while(~scanf("%d%d", &n, &m))
{
for(int i = 1; i <= m; i++)
{
scanf("%d", &a[i]);
}
n--;
ans = 0;
dfs(1, 1, 1);
printf("%I64d\n", ans);
}
return 0;
}