题意: 给定一个数n 再给m个数(m<15) 假设这m个数为 a[0],a[1].....a[m-1];
求1~n中非数组a的数的倍数的数,就是把1~n中数组a的数的倍数筛掉,剩下的数的个数就是结果。
暴力跑会超时,利用容斥原理,比如n=10,m=2,a[0]=2,a[1]=3,把1到20中所有2的倍数筛掉,
先令ans=n=20,ans=ans-n/2=10。再把1到20中所有3的倍数筛掉,ans=ans-n/3=4。
那么现在问题来了,这么筛选的话会导致2和3的公倍数进行了二次筛选,也就是6,12,18这三个数,
所以要把这三个数加回来,ans=ans+n/(2*3)=7,得出最终结果。以此类推,数的个数为奇数就减,
数的个数为偶数就加,这就是容斥原理。
注意 :本题给的数不一定为质数,也可能为合数,上述容斥原理适用于质数,
在本题中进行容斥原理时要用他们的最小公倍数lcm。
比如n=10,m=2,a[0]=2,a[1]=4,这组数据,可验证本题要用lcm,而不是a[0]*a[1]。
#include <iostream> #include <stdio.h> #include <algorithm> using namespace std; typedef long long ll; ll n,m,a[20],ans; ll gcd(ll a,ll b) //求最大公约数 { return b==0? a:gcd(b,a%b); } ll lcm(ll a,ll b) //求最小公倍数 { return a/gcd(a,b)*b; } void dfs(ll hav,ll cur,ll num) //容斥原理 { if(hav>n||cur>=m) //m=2 m!=15 return ; for(int i=cur;i<m;i++) //注意区分这里的i和主函数里的i,如果混了就错了 { ll temp=lcm(hav,a[i]); if(num&1) ans-=n/temp; //奇数减 else ans+=n/temp; //偶数加 dfs(temp,i+1,num+1); } } int main() { ll i,j; while(scanf("%lld%lld",&n,&m)!=EOF) { for(i=0;i<m;i++) cin>>a[i]; ans=n; for(i=0;i<m;i++) { ans-=n/a[i]; //奇数减 dfs(a[i],i+1,2); } cout<<ans<<endl; } return 0; }
时间: 2024-09-30 00:43:44