容斥原理

 

 

题意:
      求区间(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

*/

 

posted @ 2012-07-29 10:44  qijinbiao1  阅读(205)  评论(0编辑  收藏  举报