UVA.10325 The Lottery (组合数学 容斥原理 二进制枚举)

UVA.10325 The Lottery (组合数学 容斥原理)

题意分析

首先给出一个数n,然后给出m个数字(m<=15),在[1-n]之间,依次删除给出m个数字的倍数,求最后在[1-n]之间还剩下多少个数字(包括1和n),已知m个数字中不会包含1(否则全部都被刷掉了)。

前置技能
1. 给出数字s,在[1-n]之间,s的倍数有n/s个。
2. 给出数字s1,和s2,在[1-n]之间,既是s1的倍数,又是s2的倍数,有n/lcm(s1,s2)个.
3. 给出数字s1,s2……sk(共k个数字),在[1-n]之间,既是s1也是s2……也是sk的倍数,有n/lcm(s1,s2,s3……sk)个。
4. 结论3在si两两互质的情况下,有n/(s1* s2 * s3…… * sk)个。
5. 容斥定理

用容斥定理能求出来,s1,s2,s3……sk的倍数在[1,n]中共有多少各个,然后用n减去即可。
或者利用奇增偶减的规则,一次性枚举完也可以。

代码总览

#include <cstdio>
#include <algorithm>
#include <cstring>
#define nmax 20
#define ll long long
using namespace std;
ll initnum[nmax];
ll n;
int m;
ll gcd(ll a, ll b)
{
    if(!b) return a;
    else return gcd(b, a%b);
}
ll lcm(ll a, ll b)
{
    return a/gcd(a,b)*b;
}
int main()
{
   // freopen("in.txt","r",stdin);
    while(scanf("%lld %d",&n,&m) != EOF){
        for(int i = 0 ;i<m;++i) scanf("%lld",&initnum[i]);
        ll time  = (1<<m);
        ll ans = 0;
        for(int i = 1; i<=time ;++i){
            int index = 0;
            ll tmpans = 1LL;
            for(int j  = 0; j<m;++j){
                if( 1 & (i>>j)){
                    tmpans = lcm(tmpans,initnum[j]);
                    index++;
                }
            }
            if(index & 1){//add
                ans -= n / tmpans;
            }else{//even
                ans += n / tmpans;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}
posted @ 2017-08-08 11:06  pengwill  阅读(142)  评论(0编辑  收藏  举报