容斥定理+组合计数

7.22  

容斥定理:

问题
例子:
[A B] 区间内,与 N 互质的数的个数。
 
可以先求不互质的个数,再利用前缀和的思想 [1,B]-[1,A-1]即可
N 的质因子的倍数就不互质的
现在求[1,m]中与n不互质的个数
 
假如m=12,n=30;
 
n的质因子:2,3,5
(2,4,6,8,10,12)->n/2 6 (3,6,9,12)->n/3 4 , (5,10)->n/5 2
这里面是有重复的这样就用到 了容斥定理
记住奇加偶减
公式就是:
n/2+n/3+n/5-n/(2*3)-n/(2*5)-n/(3*5)+n/(2*3*5)
 
这个代码中有质因子分解,二进制枚举复习了一下
 
代码实现:
 
#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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值