前言
数论中所有的数通常都是正整数,在出现的字母在未经说明的情况下为正整数
模运算
有些时候答案非常大,题目会要求你输出答案取模一个数的值
对于取模,有以下定理
1、
(
a
+
b
)
%
c
=
(
a
%
c
+
b
%
c
)
%
c
(a+b)\%c=(a\%c+b\%c)\%c
(a+b)%c=(a%c+b%c)%c
2、
(
a
−
b
)
%
c
=
(
a
%
c
−
b
%
c
)
%
c
(a-b)\%c=(a\%c-b\%c)\%c
(a−b)%c=(a%c−b%c)%c
3、
(
a
∗
b
)
%
c
=
(
a
%
c
)
∗
(
b
%
c
)
%
c
(a*b)\%c=(a\%c)*(b\%c)\%c
(a∗b)%c=(a%c)∗(b%c)%c
对于符号,我们可以简化成语言
和的余数等于余数的和,差的余数等于余数的差,积的余数等于余数的积。
肯定有人问:“为什么没有商的余数等于余数的商”
对于除法而言,及时除数与被除数都为整数,但商也不一定是整数,而加减乘在
a
,
b
a,b
a,b均为整数的情况下一定是整数
整除
a
≠
0
a≠0
a=0,若存在整数
q
≠
0
q≠0
q=0使得
a
q
=
b
aq=b
aq=b,那么称a是b的因数(约数),b是a的倍数,a能整除b,b能被a整除,记为
a
∣
b
a|b
a∣b
对于整除,有以下定理
1、
a
∣
b
,
b
∣
c
⇒
a
∣
c
a|b,b|c\Rightarrow a|c
a∣b,b∣c⇒a∣c,a是b的因子,b是c的因子,那么a也就是c的因子
2、
a
∣
b
,
c
∣
d
⇒
a
c
∣
b
d
a|b,c|d\Rightarrow ac|bd
a∣b,c∣d⇒ac∣bd,a是b的因子,c是d的因子,那么ab也就是cd的因子
3、
a
∣
b
,
a
∣
c
⇒
a
∣
p
b
+
p
c
,过程:
b
=
n
a
,
c
=
m
a
,原式等于
a
∣
p
n
a
+
p
m
a
,等于
a
∣
a
(
p
n
+
p
m
)
,
∵
a
∣
a
,
∴
a
∣
p
b
+
p
c
a|b,a|c\Rightarrow a|pb+pc,过程:b=na,c=ma,原式等于a|pna+pma,等于a|a(pn+pm),∵a|a,∴a|pb+pc
a∣b,a∣c⇒a∣pb+pc,过程:b=na,c=ma,原式等于a∣pna+pma,等于a∣a(pn+pm),∵a∣a,∴a∣pb+pc
质数(prime number,又称素数)
- 质数定义:不含有除了1和自身以外的其他因子的数称为质数(素数)
- 特殊:2是最小的质数,也是唯一的偶质数
- 质数判定定理:若一个数n找不到小于等于 n \sqrt{n} n的非1因数,则这个数是质数
- 素数定理:n充分大时,n以内的素数个数约为n/log2n个
关于质数的东西有很多呀,不慌,慢慢来
整数的标准分解
一个大于1的整数一定可以唯一地写成若干个质数的幂的积
就像这样
n
=
p
1
w
1
∗
p
2
w
2
∗
p
3
w
3
∗
p
4
w
4
∗
…
…
∗
p
m
w
m
n=p_1^{w1}*p_2^{w2}*p_3^{w3}*p_4^{w4}*……*p_m^{wm}
n=p1w1∗p2w2∗p3w3∗p4w4∗……∗pmwm
- 互质(coprime):两个整数除1以外再没有其他因数,那么这两个数互质
- 两个互质的数的唯一分解中没有相同的质数
代码
void factorize(int n){
for(int i = 2; n != 1; i ++){
if( n%i == 0 ){
p[++k] = i;
while( n%i == 0 ){
n /= i;
w[k]++
}
}
}
}
最大公约数与最小公倍数
- 两个数
a
,
b
a,b
a,b的最大公约数记为
g
c
d
(
a
,
b
)
gcd(a,b)
gcd(a,b)或者
(
a
,
b
)
(a,b)
(a,b)
(记住共产党) - 两个数 a , b a,b a,b的最小公倍数记为 l c m ( a , b ) lcm(a,b) lcm(a,b)或者 [ a , b ] [a,b] [a,b]
我们用整数的标准分解来深度了解一下
g
c
d
gcd
gcd和
l
c
m
lcm
lcm
x
=
p
1
a
1
∗
p
2
a
2
∗
p
3
a
3
∗
p
4
a
4
∗
…
…
∗
p
m
a
m
x=p_1^{a1}*p_2^{a2}*p_3^{a3}*p_4^{a4}*……*p_m^{am}
x=p1a1∗p2a2∗p3a3∗p4a4∗……∗pmam
y
=
p
1
b
1
∗
p
2
b
2
∗
p
3
b
3
∗
p
4
b
4
∗
…
…
∗
p
n
b
n
y=p_1^{b1}*p_2^{b2}*p_3^{b3}*p_4^{b4}*……*p_n^{bn}
y=p1b1∗p2b2∗p3b3∗p4b4∗……∗pnbn
其中
p
n
p_n
pn和
p
m
p_m
pm足够大,
a
i
,
b
i
a_i,b_i
ai,bi可以为0
我们知道两个数的
g
c
d
gcd
gcd是两个数都有的最大因子,注意,是最大,说明
p
x
p_x
px一定取
p
x
a
x
p_x^{ax}
pxax或
p
x
b
x
p_x^{bx}
pxbx,而两个数都有,说明取
p
x
m
i
n
(
a
x
,
b
x
)
p_x^{min(ax,bx)}
pxmin(ax,bx)
设
o
=
m
a
x
(
n
,
m
)
o = max(n,m)
o=max(n,m)
∴
g
c
d
(
x
,
y
)
=
p
1
m
i
n
(
a
1
,
b
1
)
∗
p
2
m
i
n
(
a
2
,
b
2
)
∗
p
3
m
i
n
(
a
3
,
b
3
)
∗
…
…
∗
p
o
m
i
n
(
a
o
,
b
o
)
gcd(x,y)=p_1^{min(a1,b1)}*p_2^{min(a2,b2)}*p_3^{min(a3,b3)}*……*p_o^{min(ao,bo)}
gcd(x,y)=p1min(a1,b1)∗p2min(a2,b2)∗p3min(a3,b3)∗……∗pomin(ao,bo)
而两个数的
l
c
m
lcm
lcm是既有因子
x
x
x,又有因子
y
y
y, 说明
l
c
m
lcm
lcm的
p
1
x
p1_x
p1x一定满足
p
x
m
a
x
(
a
x
,
b
x
)
∣
p
1
x
p_x^{max(ax,bx)}|p1_x
pxmax(ax,bx)∣p1x,但又要求最小,所以
p
1
x
p1_x
p1x取
p
x
m
a
x
(
a
x
,
b
x
)
p_x^{max(ax,bx)}
pxmax(ax,bx)
∴
l
c
m
[
x
,
y
]
=
p
1
m
a
x
(
a
1
,
b
1
)
∗
p
2
m
a
x
(
a
2
,
b
2
)
∗
p
3
m
a
x
(
a
3
,
b
3
)
∗
…
…
∗
p
o
m
a
x
(
a
o
,
b
o
)
lcm[x,y]=p_1^{max(a1,b1)}*p_2^{max(a2,b2)}*p_3^{max(a3,b3)}*……*p_o^{max(ao,bo)}
lcm[x,y]=p1max(a1,b1)∗p2max(a2,b2)∗p3max(a3,b3)∗……∗pomax(ao,bo)
而显然
(
x
,
y
)
∗
[
x
,
y
]
=
x
∗
y
(x,y)*[x,y] = x*y
(x,y)∗[x,y]=x∗y
约数个数与约数和
我们从整数的唯一分解入手
x
=
p
1
a
1
∗
p
2
a
2
∗
p
3
a
3
∗
p
4
a
4
∗
…
…
∗
p
m
a
m
x=p_1^{a_1}*p_2^{a_2}*p_3^{a_3}*p_4^{a_4}*……*p_m^{a_m}
x=p1a1∗p2a2∗p3a3∗p4a4∗……∗pmam
我们可以知道,既然
p
1
a
1
∣
x
p_1^{a_1}|x
p1a1∣x,说明
p
1
0
∣
x
p_1^{0}|x
p10∣x,
p
1
1
∣
x
p_1^{1}|x
p11∣x,
p
1
2
∣
x
p_1^{2}|x
p12∣x……
p
1
a
1
∣
x
p_1^{a_1}|x
p1a1∣x,而这里就有
a
1
+
1
a_1+1
a1+1个约数
而既然
p
2
a
2
∣
x
p_2^{a_2}|x
p2a2∣x,说明就有
a
2
+
1
a_2+1
a2+1个约数,后面以此类推,而前面
a
n
+
1
a_n+1
an+1约数可以与后面
a
m
+
1
a_m+1
am+1个约数逐个组合,根据乘法原理,就有
(
a
n
+
1
)
∗
(
a
m
+
1
)
(a_n+1)*(a_m+1)
(an+1)∗(am+1)个约数
而根据以上结论,就可以推出
x
x
x的约数个数为
d
[
x
]
=
(
a
1
+
1
)
∗
(
a
2
+
1
)
∗
(
a
3
+
1
)
…
…
∗
(
a
m
+
1
)
d[x]=(a_1+1)*(a_2+1)*(a_3+1)……*(a_m+1)
d[x]=(a1+1)∗(a2+1)∗(a3+1)……∗(am+1)
而我们知道
x
x
x有
p
1
0
∣
x
p_1^{0}|x
p10∣x,
p
1
1
∣
x
p_1^{1}|x
p11∣x,
p
1
2
∣
x
p_1^{2}|x
p12∣x……
p
1
a
1
∣
x
p_1^{a_1}|x
p1a1∣x这a_1+1个约数,而且对于每一个
x
x
x的质因子都有
a
i
+
1
a_i+1
ai+1个约数,而这
a
1
+
a
2
+
…
…
a
m
+
m
a_1+a_2+……a_m+m
a1+a2+……am+m个约数可以多个组合,因式分解一下就可得到
s
[
x
]
=
(
1
+
p
1
1
+
p
1
2
…
+
p
1
a
1
)
∗
(
1
+
p
2
1
+
p
2
2
…
+
p
2
a
2
)
…
∗
(
1
+
p
m
1
+
p
m
2
…
+
p
m
a
m
)
s[x] = (1+p_1^{1}+p_1^{2}…+p_1^{a_1})*(1+p_2^{1}+p_2^{2}…+p_2^{a_2})…*(1+p_m^{1}+p_m^{2}…+p_m^{a_m})
s[x]=(1+p11+p12…+p1a1)∗(1+p21+p22…+p2a2)…∗(1+pm1+pm2…+pmam)
我们设
(
1
+
p
i
1
+
p
i
2
…
+
p
i
a
i
)
=
Q
(1+p_i^{1}+p_i^{2}…+p_i^{a_i}) = Q
(1+pi1+pi2…+piai)=Q为
(
1
)
(1)
(1)式,则
p
i
∗
Q
=
(
p
i
1
+
p
i
2
…
+
p
i
a
i
+
1
)
p_i*Q=(p_i^{1}+p_i^{2}…+p_i^{a_i+1})
pi∗Q=(pi1+pi2…+piai+1)为
(
2
)
(2)
(2)式,
∴
(
2
)
−
(
1
)
=
p
i
∗
Q
−
Q
=
(
p
i
−
1
)
∗
Q
=
p
i
a
i
+
1
−
1
∴(2)-(1)=p_i*Q-Q=(p_i-1)*Q=p_i^{a_i+1}-1
∴(2)−(1)=pi∗Q−Q=(pi−1)∗Q=piai+1−1
∴
Q
=
p
i
a
i
+
1
−
1
p
i
−
1
∴Q=\frac{p_i^{a_i+1}-1}{p_i-1}
∴Q=pi−1piai+1−1
欧拉筛法
埃拉托尼斯筛法相对于欧拉筛法而言太不实用,请读者自主学习
欧拉筛法如下:
void sieve(int x){
for(int i = 2; i <= n; i ++){
if( !v[i] ){
p[++cnt] = i;
v[i] = 1;
}
for(int j = 1; j <= cnt&&i*p[j] <= x; j ++){
v[i*p[j]] = 1;
if( i % p[i] == 0)
break;
}
}
}
我相信有很多读者不明白第二个循环,其实无论此时 i i i是否为质数,我们都需要把之前筛出来的素数乘 i i i来筛掉后面的素数,而欧拉筛的难点就在于对if (i % prime[j] == 0)这步的理解,当i是prime[j]的整数倍时,记 m = i / prime[j],那么 i * prime[j+1] 就可以变为 (m * prime[j+1]) * prime[j],这说明 i * prime[j+1] 是 prime[j] 的整数倍,不需要现在筛出,因为在之后筛除过程中i * prime[j+1] 这个合数一定会被prime[j]筛除,prime[j]之后的所有素数同理,所以break跳出循环。
欧拉函数求约数个数d[n]
- 若 n n n是素数,约数只有 1 , n 1,n 1,n,所以 d [ n ] = 2 d[n]=2 d[n]=2
- 若 i % p [ j ] ≠ 0 i\%p[j]\ne0 i%p[j]=0, i i i不存在 p [ j ] p[j] p[j]这个因子,说明 i ∗ p [ j ] i*p[j] i∗p[j]只包含 p [ j ] p[j] p[j]的一次方,没问题吧,而我们知道约数个数的公式是乘上每个质因子的次数+1,因为 i ∗ p [ j ] i*p[j] i∗p[j]只包含 p [ j ] p[j] p[j]的一次方, p [ j ] p[j] p[j]的次数也就是一,乘上的也就是 ( 1 + 1 ) = 2 (1+1)=2 (1+1)=2了
- 若
i
%
p
[
j
]
=
0
i\%p[j]=0
i%p[j]=0,
i
i
i至少存在
p
[
j
]
p[j]
p[j]的一次方,说明
i
∗
p
[
j
]
i*p[j]
i∗p[j]至少包含
p
[
j
]
p[j]
p[j]的二次方,没问题吧,我们用
n
u
m
[
i
]
num[i]
num[i]表示
i
i
i的最小的质因子的次数(因为我们需要通过次数来计算约数个数),则
d
[
i
]
d[i]
d[i]包含了
(
1
+
n
u
m
[
i
]
)
(1+num[i])
(1+num[i])这个因式,因为
d
[
i
]
d[i]
d[i]肯定也是从之前的某个数乘上
(
1
+
n
u
m
[
i
]
)
(1+num[i])
(1+num[i])得到的,而现在要求
d
[
i
∗
p
[
j
]
d[i*p[j]
d[i∗p[j],可以肯定的是
d
[
i
∗
p
[
j
]
]
d[i*p[j]]
d[i∗p[j]]肯定存在
n
u
m
[
i
]
+
2
num[i]+2
num[i]+2这个因式,而
d
[
i
]
d[i]
d[i]存在
n
u
m
[
i
]
+
1
num[i]+1
num[i]+1这个因式,那么如何转换呢,就是先把
n
u
m
[
i
]
+
1
num[i]+1
num[i]+1除了之后,再乘上
n
u
m
[
i
]
+
2
num[i]+2
num[i]+2,就可以推出
d
[
i
∗
p
[
j
]
]
d[i*p[j]]
d[i∗p[j]]了
因为 i ∗ p [ j ] i*p[j] i∗p[j]是在 i i i的基础上增加了 p p p的一次方,所以 n u m [ i ∗ p [ j ] ] = n u m [ i ] + 1 num[i*p[j]]=num[i]+1 num[i∗p[j]]=num[i]+1了
所以 n u m [ i ∗ p [ j ] ] = n u m [ i ] + 1 d [ i ∗ p [ j ] ] = d [ i ] / ( n u m [ i ] + 1 ) ∗ ( n u m [ i ∗ p [ j ] ] + 1 ) num[i*p[j]] = num[i]+1\\ d[i*p[j]] = d[i]/(num[i]+1)*(num[i*p[j]]+1) num[i∗p[j]]=num[i]+1d[i∗p[j]]=d[i]/(num[i]+1)∗(num[i∗p[j]]+1)
代码
void sieve(int x){
d[1] = 1;
for(int i = 2; i <= n; i ++){
if( !v[i] ){
p[++cnt] = i;
v[i] = 1;
d[i] = 2;
}
for(int j = 1; j <= cnt&&i*p[j] <= x; j ++){
v[i*p[j]] = 1;
if( i % p[i] == 0){
num[i*p[j]] = num[i]+1;
d[i*p[j]] = d[i]/(num[i]+1)*(num[i]+2);
break;
}
d[i*p[j]] = d[i]*2;
num[i*p[j]] = num[i]+1;
}
}
}
欧拉函数求约数和s[n]
- 若 i i i为质数,约数只有 i , 1 i, 1 i,1,所以 s [ i ] = i + 1 s[i] = i+1 s[i]=i+1
- 若
i
%
p
[
j
]
≠
0
i\%p[j]\ne0
i%p[j]=0,说明
i
∗
p
[
j
]
i*p[j]
i∗p[j]只含有
p
[
j
]
p[j]
p[j]的一次方,没问题吧。而因为我们知道
求约数和,就是求每个质因子等比数列的乘积,因为 i ∗ p [ j ] i*p[j] i∗p[j]只含有 p [ j ] p[j] p[j]的一次方,所以要在 s [ i ] s[i] s[i]基础上乘的就是 p [ j ] 1 p[j]^1 p[j]1的等比数列,也就是 p [ j ] 1 + p [ j ] 0 = p [ j ] + 1 p[j]^1+p[j]^0=p[j]+1 p[j]1+p[j]0=p[j]+1 - 若
i
%
p
[
j
]
=
0
i\%p[j]=0
i%p[j]=0,说明
i
∗
p
[
j
]
i*p[j]
i∗p[j]至少含有
p
[
j
]
p[j]
p[j]的二次方,我们设
p
s
u
m
[
i
]
psum[i]
psum[i]表示关于
i
i
i的最小质因子
p
p
p的等比数列求和
1
+
p
+
p
2
+
…
…
+
p
w
1+p+p^2+……+p^w
1+p+p2+……+pw因为约数和就是这东西的乘积,可以肯定的是,
s
[
i
]
=
p
s
u
m
[
i
]
s[i]=psum[i]
s[i]=psum[i]乘上另外质因子的等比数列求和。由于
i
∗
p
[
j
]
i*p[j]
i∗p[j]和
i
i
i均以
p
[
j
]
p[j]
p[j]作为最小的质因子(欧拉筛的性质,自己看代码体会吧),所以
i
∗
p
[
j
]
i*p[j]
i∗p[j]一定包含
p
s
u
m
[
i
]
∗
p
+
1
psum[i]*p+1
psum[i]∗p+1这个因式(因为
i
∗
p
[
j
]
i*p[j]
i∗p[j]比
p
[
j
]
p[j]
p[j]多一个
p
[
j
]
p[j]
p[j]的1次方,
p
s
u
m
[
i
]
=
1
+
p
+
p
2
+
…
…
+
p
w
psum[i]=1+p+p^2+……+p^w
psum[i]=1+p+p2+……+pw,所以
p
s
u
m
[
i
∗
p
[
j
]
]
=
1
+
p
+
p
2
+
…
…
+
p
w
+
1
psum[i*p[j]]=1+p+p^2+……+p^{w+1}
psum[i∗p[j]]=1+p+p2+……+pw+1,所以
p
s
u
m
[
i
∗
p
[
j
]
]
psum[i*p[j]]
psum[i∗p[j]]其实就等于
p
s
u
m
[
i
]
∗
p
[
j
]
+
1
psum[i]*p[j]+1
psum[i]∗p[j]+1,看的出来吧),那么
s
[
i
∗
p
[
j
]
]
s[i*p[j]]
s[i∗p[j]]到
s
[
i
]
s[i]
s[i]的转换就和
d
[
i
∗
p
[
j
]
]
d[i*p[j]]
d[i∗p[j]]到
d
[
i
]
d[i]
d[i]的转换差不多了
p s u m [ i ∗ p [ j ] ] = p s u m [ i ] ∗ p [ j ] + 1 psum[i*p[j]] = psum[i]*p[j]+1 psum[i∗p[j]]=psum[i]∗p[j]+1
s [ i ∗ p [ j ] ] = s [ i ] p s u m [ i ] ∗ ( p s u m [ i ∗ p [ j ] ] ) s[i*p[j]]=\frac{s[i]}{psum[i]}*(psum[i*p[j]]) s[i∗p[j]]=psum[i]s[i]∗(psum[i∗p[j]])
代码
void sieve(int x){
s[1] = 1;
for(int i = 2; i <= x; i ++){
if( !v[i] ){
p[++cnt] = i;
v[i] = 1;
psum[i] = s[i] = i+1;
}
for(int j = 1; j <= cnt&&p[j]*i <= x; j ++){
v[i*p[j]] = 1;
if( i%p[j] == 0 ){
psum[i*p[j]] = psum[i]*p[j]+1;
s[i*p[j]] = s[i]/psum[i]*psum[i*p[j]];
break;
}
s[i*p[j]] = s[i]*(p[j]+1);
psum[i*p[j]] = 1+p[j];
}
}
}