容斥定理:
#include<iostream>
#include<cstdio>
#include<map>
using namespace std;
int cnt;
int a[100];
int num;
int main()
{
int A,B,n;
cin>>A>>B>>n;
//质因子分解
for(int i=2;i*i<=n;i++){
if(n%i==0){
a[++cnt]=i;
}
while(n%i==0) n/=i;
}
if(n>1) a[++cnt]=n;
//二进制枚举选择数
int SUM=0;
for(int i=1;i<(1<<cnt);i++){//枚举2^n -1种情况
int cnt1=0;
int sum=1;
for(int j=0;j<cnt;j++){
if(i&(1<<j)){//如果第j+1位数选中的话
sum*=a[j+1];
cnt1++;
}
}
//奇加偶减
if(cnt1&1){
SUM+=B/sum;
SUM-=(A-1)/sum;
}
else{
SUM-=B/sum;
SUM+=(A-1)/sum;
}
}
cout<<(B-A+1)-SUM<<endl;
}
再来个例子做一下
题目连接:https://vjudge.net/problem/HDU-1796
Now you get a number N, and a M-integers set, you should find out how many integers which are small than N, that they can divided exactly by any integers in the set. For example, N=12, and M-integer set is {2,3}, so there is another set {2,3,4,6,8,9,10}, all the integers of the set can be divided exactly by 2 or 3. As a result, you just output the number 7.
Input
There are a lot of cases. For each case, the first line contains two integers N and M. The follow line contains the M integers, and all of them are different from each other. 0<N<2^31,0<M<=10, and the M integer are non-negative and won’t exceed 20.
Output
For each case, output the number.
Sample Input
12 2 2 3
Sample Output
7
题意:多组输入,每组输入n,m 第二行有m个数 从[1,n)找能被这个m个数整除的数的个数
m的值很小可以用二进制枚举,并且m可能为0,0的直接跳过就行了
code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
typedef long long ll;
using namespace std;
int cnt;
int main()
{
ll n,m;
while(~scanf("%lld%lld",&n,&m)){
vector<ll> ve;
ll a;
for(int i=1;i<=m;i++){
scanf("%d",&a);
if(a!=0) ve.push_back(a);
}
cnt=ve.size();
ll SUM=0;
for(int i=1;i<(1<<cnt);i++){
ll cnt1=0,sum=1;
for(int j=0;j<cnt;j++){
if(i&(1<<j)){
cnt1++;
sum=sum*ve[j]/__gcd(sum,ve[j]);
}
}
if(cnt1&1){
SUM+=(n-1)/sum;
}
else{
SUM-=(n-1)/sum;
}
}
printf("%lld\n",SUM);
}
return 0;
}
组合
直接给板子吧
数小不用取模直接递推即可
void initc(){
for(int i=0;i<=N;i++) C[i][0]=1;
for(int i=1;i<=N;i++)
for(int j=1;j<=i;j++){
C[i][j]=C[i-1][j]+C[i-1][j-1];
}
}
要取mod的话 m/s 除法不能取余 需要转换为逆元
void prepare_inv(){//普通的方法求逆元
inv[1]=1;
for(int i=2;i<=N;i++){
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}
}
Cnk
ll C(int n ,int k){
if(k>n) return 0;
if(k>n-k) k=n-k;
ll m,s;
for(int i=0;i<K;i++){
m=m*(n-i)%mod;
s=s*(i+1)%mod;
}
return m*pow_mod(s,mod-2)%mod
}
ll lucas(int a,int b){//卢卡斯定理
if(a<mod&&b<mod) return C(a,b);
else return C(a%mod,b%mod)*lucas(a/mod,b/mod)%mod;
}