题目:http://acm.hdu.edu.cn/showproblem.php?pid=1796
题意:给你两个数n(n<2^23),m(m<11),再给你一个集合S(有m个元素),找出小于n而且可以被集合S{a1,a2,a3,...am}里面一个或多个数整除的数c,统计c的个数。
分析:先不考虑重叠的部分,把小于n,且能整除a1的数的个数求出来,即(n-1)/a1,同理,求出a2,a3.....am的。然后把个数加起来,再减去重叠部分的个数就行了。
这里给出容斥原理公式:
dfs代码:
#include <iostream>
#include <cstdio>
using namespace std;
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
int lcm(int a,int b)
{
return a/gcd(a,b)*b;
}
int s[40],n,m,ans,f,cnt;
void dfs(int toget,int got,int cur_value,int cur_n)
{
if(got==toget)
{
ans+=n/cur_value*f;
return ;
}
if(cur_n>cnt)
return ;
for(int i=cur_n;i<=cnt;i++)
dfs(toget,got+1,lcm(s[i],cur_value),i+1);
}
int main()
{
int i,j,x;
while(scanf("%d%d",&n,&m)!=EOF)
{
--n;
f=1;
ans=0;
for(i=1,cnt=0;i<=m;i++)
{
scanf("%d",&x);
if(x)
s[++cnt]=x;
}
for(i=1;i<=cnt;i++,f=-f)
dfs(i,0,1,1);
printf("%d\n",ans);
}
return 0;
}
位运算代码:
#include <iostream>
#include <cstdio>
using namespace std;
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
int main()
{
int s[40];
int n,m,cnt,i,j,k,x;
int ans,count,temp,lcm;
while(scanf("%d%d",&n,&m)!=EOF)
{
--n;
for(i=1,cnt=0;i<=m;i++)
{
scanf("%d",&x);
if(x)
s[++cnt]=x;
}
ans=0;
for(i=1;i<(1<<cnt);i++)
{
count=0;
temp=i;
lcm=1;
for(j=1;j<=cnt;j++)
{
if(temp&1)
{
count++;
lcm=lcm/gcd(lcm,s[j])*s[j];
}
temp>>=1;
if(!temp)
break;
}
if(count&1)
ans+=n/lcm;
else
ans-=n/lcm;
}
printf("%d\n",ans);
}
return 0;
}