容斥原理
题意:
求区间(0,n)之间能被集合A内一个元素整除的数的个数,例如n = 12, A = {2, 3}
则能被A集合元素整除的数的集合为{2,3,4,6,8,9,10}则结果为7。
http://acm.hdu.edu.cn/showproblem.php?pid=1796
#include<iostream> #include<cstdio> using namespace std; #define LL long long int len,num[20]; LL n,sum; LL gcd(LL a,LL b) { if(b==0) return a; return gcd(b,a%b); } void dfs(LL lcm,int sta,int cnt)//最小公倍数为lcm时再加入num[sta]后的答案 { lcm=lcm*num[sta]/gcd(lcm,num[sta]); //求集合前面几个数的最小公倍数 if(cnt&1)sum+=n/lcm; else sum-=n/lcm; //这里减去重复计算的数,这时整个代码的精华 for(int i=sta+1;i<len;i++) dfs(lcm,i,cnt+1); } int main() { int m; while(cin>>n>>m) { len=0; for(int i=0;i<m;i++) { int x; cin>>x; if(x==0)continue; num[len++]=x; } n--; sum=0; for(int i=0;i<len;i++) dfs(num[i],i,1); cout<<sum<<endl; } }
二进制组合生成法(比上面的慢3倍)
#include <iostream> using namespace std; int arr[30]; int n,m; int gcd(int a,int b) { if(b==0) return a; return gcd(b,a%b); } int solv(int s) { int cnt=0; long long lcm=1; for(int i=0;i<m;i++) if(s&(1<<i)) { if(cnt==0) lcm=arr[i]; else lcm=lcm/gcd(lcm,arr[i])*arr[i]; cnt++; } int ans=0; if(cnt%2) ans=n/lcm; else ans=-n/lcm; return ans; } int main() { while(cin>>n>>m) { n--; int a,cnt=0; for(int i=0;i<m;i++) { cin>>a; if(a!=0) arr[cnt++]=a; } long long ans=0; if(cnt<m) m=cnt; for(int i=1;i<(1<<m);i++) { ans+=solv(i); } cout<<ans<<endl; } return 0; } /* 13 4 2 3 4 6 */