数论之容斥定理:1-n中不能被m个数(非互质)整除的数的个数

深有体会:离散数学是算法的核心!!

章节 - “集合元素计数”,知识点“排斥定理” <=> "容斥定理";

本文章将以老师课堂上讲的案例:“1-1000中不能被5,6,8整除的数的个数”延伸至“从自然数1-n中不能被m个整数(非互质)整除的数的个数” 和 “从自然数1-n中至少被m个数中的一个数(非互质)整除的数的个数”。

说明:本文省略二项式定理的证明过程,直接引入定理和推论;文章代码采用二进制枚举表示每种选择方案,例如01001表示选择第0个和第3个性质集合的方案,那么相对应的1-n中满足性质的个数就等于(n / lcm(p[0],p[3])),以此类推;采用辗转相除法求解最大公约数,进而求解最小公倍数

A. 引入定理3.2  S中不具有性质 $P_1, P_2, \cdots, P_m$ 的元素数是:
\begin{aligned} & \left|\bar{A}_1 \cap \bar{A}_2 \cap \cdots \cap \bar{A}_m\right| \\ = & |S|-\sum_{i=1}^m\left|A_i\right|+\sum_{1 \leqslant i<j \leqslant m}\left|A_i \cap A_j\right|- \\ & \sum_{1 \leqslant i<j<k \leqslant m}\left|A_i \cap A_j \cap A_k\right|+\cdots+(-1)^m\left|A_1 \cap A_2 \cap \cdots \cap A_m\right| \end{aligned}
 

B. 引入推论  在S中至少具有一条性质的元素数是:

\begin{aligned} & \left|A_1 \cup A_2 \cup \cdots \cup A_m\right| \\ = & \sum_{i=1}^m\left|A_i\right|-\sum_{1<i<j<m}\left|A_i \cap A_j\right|+ \\ & \sum_{1<i<j<k<m}\left|A_1 \cap A_j \cap A_1\right|-\cdots+(--1)^{m+1}\left|A_1 \cap A_2 \cap \cdots \cap A_m\right| \end{aligned}

案例1:求1-1000中不能被5,6,8整除的数的个数。

不妨设S为自然数1000以内整数的集合,P_1P_2P_3分别表示三种性质(被5、6、8整除),对于S中的任意一个元素x,只能存在以下8(2^3)种情况:只具有性质P_1,只具有性质P_2,只具有性质P_3,同时具有性质P_1P_2、/ P_2P_3 / P_1P_3(两种),同时具有/不具有性质P_1P_2P_3(三种);分别设A_1,A_2,A_3为满足P_1P_2P_3性质的元素的(小于等于1000)集合;

根据定理3.2,有式子如下:

\begin{aligned} & \left|\bar{A}_1 \cap \bar{A}_2 \cap \bar{A}_3\right| \\ = & |S|-\left(\left|A_1\right|+\left|A_2\right|+\left|A_3\right|\right)+\left(\left|A_1 \cap A_2\right|+\left|A_1 \cap A_3\right|+\right. \\ & \left.\left|A_2 \cap A_3\right|\right)-\left|A_1 \cap A_2 \cap A_3\right| \\ = & 1000-(200+166+125)+(33+25+41)-8 \\ = & 600 \end{aligned}

规律:偶数次项数要减掉,奇数次项数要加;

注意:在没有声明哪些数是互质的情况下,\left|A_1\cap A_2\right|必然是满足被lcm(5,6)整除的数的集合。

C++代码如下:

// Author: 荣荣
// Time: 2023.10.21 09:53
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int p[3] = {5,6,8};
int n = 1000,res;
LL gcd(LL a,LL b) {return ((a % b == 0) ? b : gcd(b, a % b));} 
LL lcm(LL a,LL b) {return ((a * b) / gcd(a,b));}
int main(){
	for (int i = 1; i < (1 << 3); i ++){
		int cnt = 0, pt = 1; 
		for (int j = 0; j < 3; j ++){
			if (i >> j & 1){
			    cnt ++;
			    pt = lcm(pt, p[j]);
			    if (pt > n) 
				{
					pt = -1; 
					break;
				}
			}
		}
		res = ((pt != -1) ? ((cnt & 1) ? res + n / pt : res - n / pt) : res);
	}
	cout << n - res << endl;
	return 0;
}

输出结果:

推广案例1:求1-n(n\leq 10^{9})中不能被m个整数(p[i](p[i] \leq 10^9,i \leq m = 20))整除的数的个数。

定理3.2,秒解。

C++代码如下:

// Author: 荣荣
// Time: 2023.10.21 09:53
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int M = 25;
int p[M];
int n,m,res;
LL gcd(LL a,LL b) {return ((a % b == 0) ? b : gcd(b, a % b));} 
LL lcm(LL a,LL b) {return ((a * b) / gcd(a,b));}
int main(){
	cin >> n >> m;
	for (int i = 0; i < m; i ++) cin >> p[i];
	for (int i = 1; i < (1 << m); i ++){
		int cnt = 0;
		LL pt = 1; 
		for (int j = 0; j < m; j ++){
			if (i >> j & 1){
				cnt ++;
				pt = lcm(pt, p[j]);
				if (pt > n) 
				{
					pt = -1; 
					break;
				}
			}
		}
		res = ((pt != -1) ? ((cnt & 1) ? res + n / pt : res - n / pt) : res);
	}
	cout << n - res << endl;
	return 0;
}

// 输入
1000000000 3
6000000 821123 98874522
// 输出
999998607

推论案例2:求1-n(n\leq 10^{9})中被m个整数(p[i](p[i] \leq 10^9,i \leq m = 20))中至少一个数整除的数的个数。

根据推论,秒解。

// Author: 荣荣
// Time: 2023.10.21 09:53
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int M = 25;
int p[M];
int n,m,res;
LL gcd(LL a,LL b) {return ((a % b == 0) ? b : gcd(b, a % b));} 
LL lcm(LL a,LL b) {return ((a * b) / gcd(a,b));}
int main(){
	cin >> n >> m;
	for (int i = 0; i < m; i ++) cin >> p[i];
	for (int i = 1; i < (1 << m); i ++){
		int cnt = 0;
		LL pt = 1; 
		for (int j = 0; j < m; j ++){
			if (i >> j & 1){
				cnt ++;
				pt = lcm(pt, p[j]);
				if (pt > n) 
				{
					pt = -1; 
					break;
				}
			}
		}
		res = ((pt != -1) ? ((cnt & 1) ? res + n / pt : res - n / pt) : res);
	}
	cout << res << endl;
	return 0;
}

// 输入
124552666 5
12312345 654652 546665 879465  21356464
// 输出
573

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值