题源:https://vjudge.net/problem/13388/origin
题意:给你数n,m;再给你m个数,问你从1~(n-1)中有多少个数可以被m集合中任意一个数整除。m<=20.
思路:考虑到m很小,所以直接深搜即可。核心思想是容斥,和算年份那个(LightOJ1414)差不多。搜索到奇数层时加上,偶数层时减去。 ans便是最终答案。
下面是核心代码:
void dfs(int p,ll lcm,int depth) {
//p是当前枚举到哪一个了 lcm是当前所有数的最小公倍数
//depth是枚举到的深度
if(depth&1) ans+=(n-1)/lcm;//这两行的计算步骤很重要!
else ans-=(n-1)/lcm;
for(int i=p+1; i<=cnt; i++) {
ll lcm2=Lcm(lcm,num[i]);
dfs(i,lcm2,depth+1);
}
return;
}
下面是全部代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<algorithm>
#define ll long long
#define inf 0x3f3f3f3f
#define MID (t[k].l+t[k].r)>>1
#define cl(a,b) memset(a,b,sizeof(a))
#define dbg printf("aaa\n")
using namespace std;
//HDU1796
int n,m;
ll num[30];//也可用二进制枚举 不过感觉二进制有点麻烦
ll cnt,ans;//cnt记录去掉0之后有多少个!!!
ll Lcm(ll a,ll b) {
return a/__gcd(a,b)*b;//先除 防止爆
}
void dfs(int p,ll lcm,int depth) {
//p是当前枚举到哪一个了 lcm是当前所有数的最小公倍数
//depth是枚举到的深度
if(depth&1) ans+=(n-1)/lcm;
else ans-=(n-1)/lcm;
for(int i=p+1; i<=cnt; i++) {
ll lcm2=Lcm(lcm,num[i]);
dfs(i,lcm2,depth+1);
}
return;
}
int main() {
while(cin>>n>>m) {
ans=0;
cnt=0;
cl(num,0);
for(int i=1; i<=m; i++) {
int temp;
cin>>temp;//如果不是0 才计入 你写反了!!!
if(temp) num[++cnt]=temp;
}
for(int i=1; i<=cnt; i++) {
dfs(i,num[i],1);
}
cout<<ans<<endl;
}
return 0;
}