WJX博客

学习笔记——约数

前言

约数是一章说难不难,说简单不简单的数论章节,从这里开始,我们将面对一大堆代码简单证明复杂的数论题,提升我们的逻辑严密性,向数学的高峰发起冲锋吧!

一些概念

定义

若整数\(n\)除以整数\(d\)的余数为\(0\),即\(d\)能整除\(n\),则称\(d\)\(n\)的约数,\(n\)\(d\)的倍数,记作\(d|n\)

算术基本定理的推论

在算术基本定理中,若正整数\(N\)被唯一分解为\(N = p_1^{c_1} p_2^{c_2}...p_m^{c_m}\),其中\(c_i\)都是正整数,\(p_i\)都是质数,且满足\(p_1 < p_2 ... < p_m\),则\(N\)的正约数集合可写作:

\[{p_1^{b_1}p_2^{b_2}...p_m^{b_m}} \]

其中\(0 \le b_i \le c_i\)

\(N\)个正约数的个数为

\[(c_1+1) \times (c_2+1) \times ... \times (c_m+1) = \prod\limits_{i=1}^m(c_i+1) \]

\(N\)的所有正约数的和为:

\[(1+p_1+p_1^2+...+p_1^{c_1}) \times ... \times (1+p_m+p_m^2+...+p_m^{c_m}) = \prod\limits_{i=1}^m(\sum\limits_{j=0}^{c_i}(p_i)^j) \]

理解:算术基本定理告诉我们每个数都可以分解为有限个质因数乘积,我们可以类比多重背包的思想,每一个质数取或不取以及取几个都可以产生一种新的状态,不同的状态表现在数上就是有不同的数,现在可以理解正约数集合和正约数的个数的公式了吧。在正约数的个数的公式的基础上,我们可以将个数改为值,则\(c_i+1\)个数分别对应\(1,p_i,p_i^2...p_i^{c_i}\),就得到了正约数的和的公式。

\(N\)的正约数集合

类似素数判断,我们枚举\([1,\sqrt{n}]\),统计答案即可。

推论:一个数最多有\(2 \times \sqrt{n}\)个约数。

代码如下:

int div[N],m=0;
//div数组记录约数集合,m记录约数个数 
il void get_divisor(int n) {
	for(re int i=1;i*i<=n;++i) {
	//从1开始,因为1是任何数的一个约数 
		if(n%i==0) {
			div[++m]=i;
			if(i!=n/i) div[++m]=n/i;
			//判断是否是平方数,不是的话约数成对出现,在数组中加入n/i 
		}
	}
}

\(1 \sim N\)的正约数集合

因为用上述方法会达到\(O(n \sqrt{n})\)的复杂度,所以我们可以换中思路,枚举\(1 \sim n\),并将它们在\(n\)以内的倍数标记即可。

代码如下:

vector <int> div[N];
il void get_divisor(int n) {
	for(re int i=1;i<=n;++i) //枚举1到n 
		for(re int j=1;j<=n/i;++j) //枚举i在n的范围内的数 
			div[i+j].push_back(i);//统计答案 
}	

因为上述代码的时间复杂度为\(O(n + \frac{n}{2} + \frac{n}{3} + ... + \frac{n}{n}) = O(nlogn)\),所以\(1 \sim n\)的每个数的约数个数之和大约为\(O(nlogn)\)

AcWing198. 反素数(题解)(我的代码)

本题我们要先理解定义:不超过\(N\)的最大的反素数是\(1 \sim N\)中约数个数最多的数中最小的,再从数据范围分析,可以得到反素数的不同质因子数不超过\(10\)且指数和不超过三十(\(2 \times 3 \times 5 \times 7 \times 11 \times 13 \times 17 \times 19 \times 23 \times 29 \times 31 > 2 \times 10^9\)\(2^{31} > 2 \times 10^9\))并且质因子的次数满足非严格单调减(否则可以用更小的数换掉),最后用\(dfs\)实现即可。

AcWing199. 余数之和(题解)(我的代码)

因为\(k\) \(mod\) \(i\) \(= k - \left\lfloor\dfrac{k}{i}\right\rfloor \times i\),而\(\left\lfloor\dfrac{k}{i}\right\rfloor\)会在一些区间内取得相同的值,所以我们可以找出那些区间,并在这些区间上运用等差数列求和公式。

最大公约数

定义

若自然数\(d\)同时是自然数\(a\)\(b\)的约数,则称\(d\)\(a\)\(b\)的公约数。在\(a\)\(b\)中所有公约数中最大的称为最大公约数,记为\(gcd(a,b)\)

同理,最小公倍数即为自然数\(a\)\(b\)的所有公倍数中最小的称为最小公倍数。记作\(lcm(a,b)\)

我们也可以根据这个定义更多数的最大公约数和最小公倍数。

定理

\[\forall a,b \in \mathbf{N}, gcd(a,b) \times lcm(a,b) = a \times b \]

证明:设\(d = gcd(a,b),a_0=a/d,b_0=b/d\),则\(gcd(a_0,b_0) = 1,lcm(a_0,b_0)=a_0 \times b_0\)

所以\(lcm(a,b) = lcm(a_0 \times d,b_0 \times d)=lcm(a_0,b_0) \times d =a_0 \times b_0 \times d=a \times b /d\)

更相减损术

\[\forall a,b \in \mathbf{N},a \ge b, gcd(a,b) = gcd(a,a-b) = gcd(b,a-b) \]

\[\forall a,b \in \mathbf{N},gcd(2 \times a,2 \times b) = 2 \times gcd(a,b) \]

证明:对于\(a,b\)的任意公约数\(d\),有\(d|a,d|b\),则\(d|(a-b)\)

欧几里得算法

\[\forall a,b \in \mathbf{N},b \ne 0,gcd(a,b)=gcd(b,a \% b) \]

证明:

  • \(a < b\)时,显然\(a \% b = a\),成立

  • \(a \ge b\)时,设\(a=q \times b + r\),有\(d|a,d|q \times b\),则\(d|(a-q \times b)\),即\(d | r\)

代码如下:

il int get_gcd(int a,int b) {
	return b?gcd(b,a%b):a;
	//终止条件:如果b被模完了,那a就是最小公倍数
	//因为如果b能被模完,那前一次的b一定是前一次的a的因数
	//剩下的就是最大公因数 
}	

P1072 [NOIP2009 提高组] Hankson 的趣味题(题解)(我的远古的代码(码风变化有点大))

因为\(a,b,c,d,x\)都可以质因数分解,所以我们只需要根据其质因数的个数来进行分类讨论即可(设为\(m_a,m_b,m_c,m_d,m_x\))。

对于\(gcd(a,x)=c\)

  • \(m_a>m_c\)时,只能\(m_x=m_c\)(把公共质因子数降下来)

  • \(m_a=m_c\)时,只需要满足\(m_x \ge m_c\)(反正有\(m_a\)会把公共质因子数降下来)

  • \(m_a<m_c\)时,无解(我\(m_x\)就是提的再高也会被\(m_a\)拖下来,达不到\(m_c\)

同理,对于\(lcm(b,x)=d\)

  • \(m_b<m_d\)时,只能\(m_x=m_d\)(把公共质因子数提上去)

  • \(m_b=m_d\)时,只需要满足\(m_x \le m_d\)(反正有\(m_b\)会把公共质因子数提上去)

  • \(m_b<m_d\)时,无解(我\(m_x\)就是降得再低也会被\(m_a\)提上去,达不到\(m_c\)

综上,有两种情况有解

  • \(m_a \ge m_c,m_b \le m_d,m_c \le m_d\)\(m_a\)\(m_c\)\(m_b\)\(m_d\)和$m_c \(与\) m_d$三者只有一者取等,都只有一种取法。

  • \(m_a = m_c,m_b = m_d,m_c \le m_d\),则\(m_x\)可以取\(m_c \sim m_d\)中的任意值,有\(m_d - m_c +1\)中取法。

欧拉函数

互质的定义

\[\forall a,b, gcd(a,b)=1 \]

欧拉函数的定义

\(1 \sim N\)中与\(N\)互质的数的个数,记作\(\varphi (N)\)

又因为在算术基本定理中,\(N={p_1^{b_1}p_2^{b_2}...p_m^{b_m}}\),所以

\[\varphi (N) = N \times \frac{p_1-1}{p_1} \times ... \times \frac{p_m-1}{p_m} = N \times \prod\limits_{p|N}(1-\frac{1}{p_i}) \]

其中\(q\)为质数。

证明:设\(p,q\)\(N\)的质因子,则\(1 \sim N\)\(p\)的倍数\(p,2 \times p... \dfrac{N}{p} \times p\)都不与\(N\)互质,这样的数有\(\dfrac{N}{p}\)个,同理\(q\)的倍数也有\(\dfrac{N}{q}\)个,但他们会重复\(\dfrac{N}{pq}\)个,所以我们要用容斥将他们加回来,即\(N\)中不与\(N\)互质的数有

\[N - \dfrac{N}{p} - \dfrac{N}{q} + \dfrac{N}{pq}= N (1 - \dfrac{1}{p})(1 - \dfrac{1}{q}) \]

同理,我们可以推广到\(N\)的所有质数。

il int phi(int n) {//求n的欧拉函数
	int ans=n;
	for(re int i=2;i*i<=n;++i) {
		if(n%i==0) {//找n的每一个质因数 
			ans=ans/i*(i-1);//统计答案 
			while(n%i==0) n/=i;	//将n中的该质因数除尽 
		}
	}
	if(n>1) ans=ans/n*(n-1);//如果n是质数,再统计入答案内 
	return ans; 
}

欧拉函数的性质:

  • \(\forall n>1\)\(1 \sim n\)中与\(n\)互质的数的和为\(n \times \varphi(n) /2\)

  • \(a,b\)互质,则\(\varphi(ab) = \varphi(a) \times \varphi(b)\)

证明:

因为\(gcd(a,b) = gcd(a,a-b)\)(更相减损术),所以不与\(n\)互质的数成对出现,平均值为\(\frac{n}{2}\),所以与\(n\)互质的数的平均值也为\(\frac{n}{2}\),即定理\(1\)成立

因为\(a,b\)之间没有相同的质因数,所以可以直接相乘而不用用容斥去重。

积性函数

\(a,b\)互质时,有\(f(ab)= f(a) \times f(b)\),则称\(f\)为积性函数

性质:

  • \(f\)是积性函数,且在算术基本定理中\(n=\prod_{i=1}^mp_i^{c_i}\),则\(f(n) = \prod_{i=1}^mf(p_i^{c_i})\)

  • \(p\)为质数且\(p|n\)\(p|n^2\),则\(\varphi(n) = \varphi(n/p) \times p\)

  • \(p\)为质数且\(p|n\)\(p \nmid n^2\),则\(\varphi(n) = \varphi(n/p) \times (p-1)\)

  • \(\sum_{d | n}\varphi(d)=n\)

证明:

1.因为是质因数分解,显然每一个质数都互质,按定义拆开即可。

2.因为\(n,n/p\)都包含相同的质因子\(p\),所以分别将\(\varphi(n),\varphi(n/p)\)展开即可。

3.因为\(n/p\)\(p\)互质且\(p\)为质数,所以\(\varphi(n) = \varphi(n/p) \times \varphi(p)= \varphi(n/p) \times (p-1)\)

4.设\(f(n)=\sum_{d | n}\varphi(d)\),若\(n,m\)互质,则\(f(nm)=\sum_{d | nm}\varphi(d) = \sum_{d | n}\varphi(d) \times \sum_{d | m}\varphi(d) = f(n) \times f(m)\),符合积性函数定义。对于单个质因子\(p^m\)\(f(p^m)=\sum_{d | p^m}\varphi(d)= \varphi(1) \times \varphi(p) \times ... \times \varphi(p^m) = 1 + (p-1)+(p-1) \times p + ... + (p-1) \times p^{m-1} = p^m\),所以合在一起就是\(\prod_{i=1}^mf(p_i^{c_i}) = \prod_{i=1}^mp_i^{c_i}= n\)

有了上述结论,我们就可以愉快的线性求出每个数的欧拉函数了。我们可以仿照线性筛的思想,因为每个数只会被它的最小质因子筛一次,所以结合2,3条结论,我们可以得到以下代码:

int vis[N],pri[N],phi[N],m;
//vis,pri,m同线性筛一样
//phi[i]记录i的欧拉函数值
il void get_phi(int n) {
	for(re int i=2;i<=n;++i) {
		if(!vis[i]) {
			vis[i]=i,pri[++m]=i;
			phi[i]=i-1;//i是质数,欧拉函数值为i-1
		}	
		for(re int j=1;j<=m;++j) {
			if(pri[j]>vis[i]||pri[j]>n/i) break;
			vis[i*pri[j]]=pri[j];
			phi[i*pri[j]]=phi[i]*(i%pri[j]?pri[j]-1:pri[j]);
            //这里的i相当于n/p,上面这句话的意思是:
			//如果p|(n/p),即p^2|n,则n的欧拉函数就是n/p的欧拉函数乘以p(结论2) 
			//如果p不整除(n/p),即p^2不整除n,则n的欧拉函数就是n/p的欧拉函数乘以p-1(结论3) 
		}	
	}	
}

P2158 [SDOI2008]仪仗队(题解)(我的代码)

本题要我们求可以看到的人数,显然就是求\(gcd(x,y)=1\)的不同\(x,y\)的对数,我们可以把矩阵看成对称的两半,只要求出$\sum_{i=1}^n \varphi(i) \times 2 +1 $即可。

P2568 GCD(题解)(我的代码)

本题要求\(gcd(x,y)\)为素数的对数,对于\(gcd(x,y)=1\),我们可以在其两边同时乘以一个素数\(p\),使得\(gcd(x \times p,y \times p) = p\),所以我们可以求出\(1 \sim n\)的所有欧拉函数值,并记录其前缀和,对于每一个质数\(p\)\(1 \sim n/p\) 中所有整数的欧拉函数值就是质数\(p\)对答案的贡献。

参考资料

《算法竞赛进阶指南》

posted @ 2021-08-05 16:04  WJX3078  阅读(278)  评论(0)    收藏  举报