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;
}