容斥原理

 

 

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



*/

 

你可能感兴趣的:(原理)