学习笔记——约数
前言
约数是一章说难不难,说简单不简单的数论章节,从这里开始,我们将面对一大堆代码简单证明复杂的数论题,提升我们的逻辑严密性,向数学的高峰发起冲锋吧!
一些概念
定义
若整数\(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\)的正约数集合可写作:
其中\(0 \le b_i \le c_i\)。
\(N\)个正约数的个数为
\(N\)的所有正约数的和为:
理解:算术基本定理告诉我们每个数都可以分解为有限个质因数乘积,我们可以类比多重背包的思想,每一个质数取或不取以及取几个都可以产生一种新的状态,不同的状态表现在数上就是有不同的数,现在可以理解正约数集合和正约数的个数的公式了吧。在正约数的个数的公式的基础上,我们可以将个数改为值,则\(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)\)。
本题我们要先理解定义:不超过\(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\)实现即可。
因为\(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)\)。
我们也可以根据这个定义更多数的最大公约数和最小公倍数。
定理
证明:设\(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\)。
更相减损术
证明:对于\(a,b\)的任意公约数\(d\),有\(d|a,d|b\),则\(d|(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\)中取法。
欧拉函数
互质的定义
欧拉函数的定义
\(1 \sim N\)中与\(N\)互质的数的个数,记作\(\varphi (N)\)。
又因为在算术基本定理中,\(N={p_1^{b_1}p_2^{b_2}...p_m^{b_m}}\),所以
其中\(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\)的所有质数。
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)
}
}
}
本题要我们求可以看到的人数,显然就是求\(gcd(x,y)=1\)的不同\(x,y\)的对数,我们可以把矩阵看成对称的两半,只要求出$\sum_{i=1}^n \varphi(i) \times 2 +1 $即可。
本题要求\(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\)对答案的贡献。
参考资料
《算法竞赛进阶指南》


浙公网安备 33010602011771号