根据心情在这里收录一些数论知识。
这应该是对数论小白极友好的数论入门博客了。(如果你不按顺序看的话我可不保证看得懂qwq)
随时更新。
文章目录
基础中的基础
如果想要获得良好的阅读体验,以下一点符号有必要知道:
- a ∣ b a|b a∣b,可以理解为 a a a 整除 b b b 或 a a a 是 b b b 的因子
- gcd \gcd gcd,表示最大公约数,用法为 gcd ( a , b ) \gcd(a,b) gcd(a,b),有时也缩写成 ( a , b ) (a,b) (a,b)
- l c m lcm lcm,表示最小公倍数,用法为 l c m ( a , b ) lcm(a,b) lcm(a,b),有时写缩写成 [ a , b ] [a,b] [a,b]
- ( l , r ) (l,r) (l,r) 和 [ l , r ] [l,r] [l,r] 有时也分别表示开区间和闭区间,这个结合情况分析
- n ! n! n! 表示 n n n 的阶乘,即 n ! = 1 × 2 × 3 × . . . × ( n − 1 ) × n n!=1\times 2 \times 3 \times ... \times (n-1) \times n n!=1×2×3×...×(n−1)×n
- 要知道集合的基本知识,比如说 ∈ , Z , Z ∗ , p r i m e \in,Z,Z^*,prime ∈,Z,Z∗,prime 的意思。
- 请务必带上脑子再往下看qwq
排列组合
全排列
n n n 个数的全排列是指:这 n n n 个数的所有不同的排列。
比如说,三个数
1
,
2
,
3
1,2,3
1,2,3 的全排列为:
1
,
2
,
3
1,2,3
1,2,3
1
,
3
,
2
1,3,2
1,3,2
2
,
1
,
3
2,1,3
2,1,3
2
,
3
,
1
2,3,1
2,3,1
3
,
1
,
2
3,1,2
3,1,2
3
,
2
,
1
3,2,1
3,2,1
一个组合数学的基础知识: n n n 个数的全排列数量为 n ! n! n!,比如上面的, 3 3 3 个数的全排列数量就有 1 × 2 × 3 = 6 1\times 2 \times 3=6 1×2×3=6 种。
证明:
设 f [ i ] f[i] f[i] 为 i i i 个数的全排列数量。
那么显然有: f [ i ] = f [ i − 1 ] × i f[i]=f[i-1]\times i f[i]=f[i−1]×i,因为考虑在 i − 1 i-1 i−1 个数的全排列里面插入 i i i 这个数,一共有 i i i 个位置可以插,所以方案数为 f [ i − 1 ] × i f[i-1] \times i f[i−1]×i。
所以 i i i 个数的全排列方案数为 i ! i! i!。
组合数
卢卡斯定理
卡特兰数
同余
这里的同余,是指余数相同。
取模
取模运算,表示取某个数除以另一个数的余数。
比如说, a m o d b = c a\bmod b =c amodb=c,就是 a a a 模 b b b 等于 c c c,这个柿子可以理解为 a a a 除以 b b b 余 c c c。
取模运算的运算等级和乘除法相同,也就是说,在一条只有乘、除、取模运算的柿子中,我们需要从左到右依次进行计算。
运算性质
1、 不满足交换律,结合律,分配率,证明很显然,随手就能找出例子。
2、 ( a m o d p ) × ( b m o d p ) m o d p = a × b m o d p (a\bmod p)\times (b\bmod p) \bmod p=a\times b \bmod p (amodp)×(bmodp)modp=a×bmodp
这个不难证明,但首先要知道, p p p 的倍数模 p p p 都等于 0 0 0。
设
a
=
x
1
p
+
y
1
,
b
=
x
2
p
+
y
2
(
y
1
,
y
2
<
p
)
a=x_1p+y_1,b=x_2p+y_2~(y_1,y_2<p)
a=x1p+y1,b=x2p+y2 (y1,y2<p),那么上面的柿子等于:
(
(
x
1
p
+
y
1
)
m
o
d
p
)
×
(
(
x
2
p
+
y
2
)
m
o
d
p
)
m
o
d
p
=
(
(
x
1
p
+
y
1
)
×
(
x
2
p
+
y
2
)
)
m
o
d
p
y
1
y
2
m
o
d
p
=
(
x
1
x
2
p
2
+
x
1
y
2
p
+
y
1
x
2
p
+
y
1
y
2
)
m
o
d
p
y
1
y
2
m
o
d
p
=
y
1
y
2
m
o
d
p
\begin{aligned} ((x_1p+y_1)\bmod p) \times ((x_2p+y_2)\bmod p) \bmod p&=((x_1p+y_1)\times (x_2p+y_2)) \bmod p\\ y_1y_2\bmod p&=(x_1x_2p^2+x_1y_2p+y_1x_2p+y_1y_2)\bmod p\\ y_1y_2\bmod p&=y_1y_2\bmod p\\ \end{aligned}
((x1p+y1)modp)×((x2p+y2)modp)modpy1y2modpy1y2modp=((x1p+y1)×(x2p+y2))modp=(x1x2p2+x1y2p+y1x2p+y1y2)modp=y1y2modp
同余
隆重请出同余号—— ≡ \equiv ≡!
这个符号还表示恒等于,但是这里不多做讨论。
这个运算符表示等式左右在模某个数的意义下相等,标准用法:
a
≡
b
(
m
o
d
p
)
a\equiv b \pmod p
a≡b(modp)
同余号和后面的 ( m o d p ) \pmod p~~ (modp) 是标配,表示 a a a 和 b b b 在模 p p p 意义下相等。
运算性质
1、 如果有
a
≡
b
(
m
o
d
p
)
,
c
≡
d
(
m
o
d
p
)
a\equiv b \pmod p,c\equiv d \pmod p
a≡b(modp),c≡d(modp),那么有
a
+
c
≡
b
+
d
(
m
o
d
p
)
a+c\equiv b+d \pmod p
a+c≡b+d(modp)
2、 如果有
a
≡
b
(
m
o
d
p
)
,
c
≡
d
(
m
o
d
p
)
a\equiv b \pmod p,c\equiv d \pmod p
a≡b(modp),c≡d(modp),那么有
a
c
≡
b
d
(
m
o
d
p
)
ac\equiv bd \pmod p
ac≡bd(modp)
这两条很显然,就不证明了。
3、 如果有 a c ≡ b c ( m o d p ) ac\equiv bc \pmod p ac≡bc(modp),那么有 a ≡ b ( m o d p d ) a\equiv b \pmod {\frac p d} a≡b(moddp),其中 d = gcd ( c , p ) d=\gcd(c,p) d=gcd(c,p)。
证明:
∵
a
c
≡
b
c
(
m
o
d
p
)
\because ac\equiv bc \pmod p
∵ac≡bc(modp)
∴
a
c
−
b
c
≡
0
(
m
o
d
p
)
\therefore ac-bc\equiv 0 \pmod p
∴ac−bc≡0(modp)
即
p
∣
(
a
c
−
b
c
)
p|(ac-bc)
p∣(ac−bc)
∴
p
∣
c
(
a
−
b
)
\therefore p|c(a-b)
∴p∣c(a−b)
∴
p
d
∣
c
d
(
a
−
b
)
\therefore\frac p d|\frac c d(a-b)
∴dp∣dc(a−b)
∵
(
p
d
,
c
d
)
=
1
\because (\frac p d,\frac c d)=1
∵(dp,dc)=1
∴
p
d
∣
(
a
−
b
)
\therefore \frac p d|(a-b)
∴dp∣(a−b)
即
a
≡
b
(
m
o
d
p
d
)
a \equiv b \pmod {\frac p d}
a≡b(moddp)。
逆元
我们知道,取模操作不能涉及小数,否则没有意义。
比如说,给一个柿子:
5
÷
3
5\div 3
5÷3,大家都知道这个东西商为
1
1
1,余数为
2
2
2,也就是:
5
÷
3
=
1
⋯
2
5\div 3 = 1 \cdots2
5÷3=1⋯2
假如我们引入了小数,那么柿子可以是:
5
÷
3
=
1.2
⋯
1.4
5\div 3 = 1.2 \cdots 1.4
5÷3=1.2⋯1.4
不仅是这样,还可以有很多奇奇怪怪的变化,那么余数就失去了其意义。
于是得到结论:取模操作不能涉及小数。
那么怎么处理取模运算中的除法呢?
我们学过一个叫倒数的东西:对于一个数 a a a,假如存在数 b b b 满足 a b = 1 ab=1 ab=1,那么称 b b b 为 a a a 的倒数。
以及我们知道倒数有个优秀的性质:除以一个数等于乘这个数的倒数。
于是取模运算中,也发明了这样一个东西,其名曰——逆元。
对于一个数 x x x,假如存在 a x ≡ 1 ( m o d p ) ax\equiv 1 \pmod p ax≡1(modp),那么称 a a a 为 x x x 在模 p p p 意义下的逆元。
于是在取模意义下,所有除以 x x x 的操作都可以换成乘以 a a a。
存在性判断
当满足 ( a , p ) = 1 (a,p)=1 (a,p)=1 时, a a a 在模 p p p 意义下才有逆元。
证明在后面的扩展欧几里得算法里。
费马小定理
对于质数 p p p,有 a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1 \pmod p ap−1≡1(modp),其中 a a a 是一个正整数。
证明
假设现在有一个序列 1 , 2 , 3 , 4 , . . . , p − 2 , p − 1 1,2,3,4,...,p-2,p-1 1,2,3,4,...,p−2,p−1。
假设将这个序列乘以 a a a,然后对 p p p 取模,那么就变成: a , 2 a , 3 a , . . . , ( p − 1 ) a ( m o d p ) a,2a,3a,...,(p-1)a \pmod p a,2a,3a,...,(p−1)a(modp)。
可以证明,此时的序列中没有相同的数。
证明: 考虑反证法。
设 x , y x,y x,y 满足 a x ≡ a y ( m o d p ) ax\equiv ay \pmod p ax≡ay(modp) 且 x ≠ y x\neq y x=y。
根据同余的运算性质3,此时 ( a , p ) = 1 (a,p)=1 (a,p)=1,所以有 x ≡ y ( m o d p ) x\equiv y \pmod p x≡y(modp)。
但是我们一开始构造的序列中是没有相同的数的,所以 x ≡ y ( m o d p ) x \equiv y \pmod p x≡y(modp) 不成立。所以,不存在这样的 x , y x,y x,y,证毕。
因为这个序列有 p − 1 p-1 p−1 项,在模 p p p 意义下没有相同的数,且这里面没有 p p p 的倍数,所以这个序列是 1 1 1 ~ p − 1 p-1 p−1 的一个排列。
那么有
1
×
2
×
3
×
.
.
.
×
(
p
−
1
)
≡
a
×
2
a
×
3
a
×
.
.
.
×
(
p
−
1
)
a
(
m
o
d
p
)
1
×
2
×
3
×
.
.
.
×
(
p
−
1
)
≡
1
×
2
×
3
×
.
.
.
×
(
p
−
1
)
×
a
p
−
1
(
m
o
d
p
)
1
≡
a
p
−
1
(
m
o
d
p
)
\begin{aligned} 1\times 2 \times 3 \times ... \times(p-1)& \equiv a\times 2a \times 3a \times ... \times (p-1)a \pmod p\\ 1\times 2 \times 3 \times ... \times(p-1)& \equiv 1\times 2 \times 3 \times ... \times (p-1) \times a^{p-1} \pmod p\\ 1&\equiv a^{p-1} \pmod p\\ \end{aligned}
1×2×3×...×(p−1)1×2×3×...×(p−1)1≡a×2a×3a×...×(p−1)a(modp)≡1×2×3×...×(p−1)×ap−1(modp)≡ap−1(modp)
得证。
应用
根据费马小定理,有
a
p
−
1
≡
1
(
m
o
d
p
)
a^{p-1}\equiv 1 \pmod p
ap−1≡1(modp)
其中
p
∈
p
r
i
m
e
p\in prime
p∈prime,变化一下:
a
p
−
2
×
a
≡
1
(
m
o
d
p
)
a^{p-2} \times a \equiv 1 \pmod p
ap−2×a≡1(modp)
那么可以知道, a p − 2 a^{p-2} ap−2 就是 a a a 在模 p p p 意义下的逆元。
费马小定理(我觉得)最重要的用法就是这个了,以后也会广泛用到。
要注意,如果只是满足 ( a , p ) = 1 (a,p)=1 (a,p)=1 而不满足 p ∈ p r i m e p\in prime p∈prime,那么即使 a a a 在模 p p p 意义下有逆元,也不能用费马小定理来求。
BSGS算法
Baby-step Giant-step 算法,是用来求解这个方程的: a x ≡ n ( m o d p ) a^x\equiv n\pmod p ax≡n(modp),其中 p p p 是质数。
由于 x x x 一定在 [ 0 , p − 2 ] [0,p-2] [0,p−2] 内,设 x = A ⌈ p ⌉ − B x=A\lceil\sqrt p\rceil-B x=A⌈p⌉−B,其中 B ∈ [ 0 , ⌈ n ⌉ ) , A ∈ [ 1 , ⌈ n ⌉ ] B\in[0,\lceil \sqrt n \rceil),A\in[1,\lceil \sqrt n \rceil] B∈[0,⌈n⌉),A∈[1,⌈n⌉]。
那么有 a A ⌈ p ⌉ − B ≡ n ( m o d p ) ⇒ a A ⌈ p ⌉ ≡ n a B ( m o d p ) a^{A\lceil \sqrt p \rceil-B}\equiv n\pmod p\Rightarrow a^{A\lceil \sqrt p\rceil}\equiv na^B\pmod p aA⌈p⌉−B≡n(modp)⇒aA⌈p⌉≡naB(modp)。
于是我们可以枚举 B B B,将所有 n a B na^B naB 放到哈希表里面,然后再枚举 A A A,看看哈希表里面是否存在 a A ⌈ p ⌉ a^{A\lceil \sqrt p\rceil} aA⌈p⌉ 这个值即可。时间复杂度 O ( p log p ) O(\sqrt p\log p) O(plogp)。
代码如下:
#include <cstdio>
#include <cstring>
#include <map>
#include <cmath>
#include <algorithm>
using namespace std;
int mod,a,n;
int ksm(int x,long long y){int re=1;for(;(y&1?re=1ll*re*x%mod:0),y;y>>=1,x=1ll*x*x%mod);return re;}
map<int,int> mp;
int main()
{
scanf("%d %d %d",&mod,&a,&n);
int sz=ceil(sqrt(mod));
int ans=-1;
for(int i=0;i<sz;i++){
int x=1ll*n*ksm(a,i)%mod;
if(!mp.count(x))mp[x]=i;
}
for(int i=1;i<=sz;i++){
int x=ksm(a,1ll*i*sz);
if(mp.count(x)){ans=i*sz-mp[x];break;}
}
if(ans!=-1)printf("%d",ans);
else puts("no solution");
}
快速幂&快速乘
欧几里得算法
扩展欧几里得算法
欧拉定理
扩展欧拉定理
首先当然是要学了上面的欧拉定理。
欧拉定理只适用于
a
a
a 与
p
p
p 互质的情况,而扩展欧拉定理则更加广泛:
a
c
≡
{
a
c
m
o
d
φ
(
p
)
(
gcd
(
a
,
b
)
=
1
)
a
c
(
gcd
(
a
,
p
)
≠
1
,
c
<
φ
(
p
)
)
a
c
m
o
d
φ
(
p
)
+
φ
(
p
)
(
gcd
(
a
,
p
≠
1
)
,
c
≥
φ
(
p
)
)
(
m
o
d
p
)
a^c\equiv \begin{cases} a^{c\bmod \varphi(p)}~~~~~~~~~~(\gcd(a,b)=1)\\ a^c~~~~~~~~~~~~~~~~~~~~~~~(\gcd(a,p)\neq 1,c< \varphi(p))\\ a^{c\bmod \varphi(p)+\varphi(p)}~~~(\gcd(a,p\neq 1),c\geq \varphi(p)) \end{cases} \pmod p
ac≡⎩⎪⎨⎪⎧acmodφ(p) (gcd(a,b)=1)ac (gcd(a,p)=1,c<φ(p))acmodφ(p)+φ(p) (gcd(a,p=1),c≥φ(p))(modp)
证明:
第一条就是欧拉定理的简单应用。
第二条没什么应用。
重点是第三条,考虑将 p p p 分解为 p 1 c 1 p 2 c 2 . . . p k c k p_1^{c_1}p_2^{c_2}...p_k^{c_k} p1c1p2c2...pkck,如果能证明 ∀ i ∈ [ 1 , k ] \forall i\in[1,k] ∀i∈[1,k],都有 a c ≡ a c m o d φ ( p ) + φ ( p ) ( m o d p i c i ) a^c\equiv a^{c\bmod \varphi(p)+\varphi(p)} \pmod {p_i^{c_i}} ac≡acmodφ(p)+φ(p)(modpici),而由于 x ≡ y ( m o d m 1 ) x\equiv y\pmod {m_1} x≡y(modm1) 和 x ≡ y ( m o d m 2 ) x\equiv y\pmod {m_2} x≡y(modm2) 可以推出 x ≡ y ( m o d m 1 m 2 ) x\equiv y\pmod {m_1m_2} x≡y(modm1m2),那么即可证得 a c ≡ a c m o d φ ( p ) + φ ( p ) ( m o d p ) a^c\equiv a^{c\bmod \varphi(p)+\varphi(p)} \pmod p ac≡acmodφ(p)+φ(p)(modp)。
分类讨论一下,若 gcd ( a , p i c i ) = 1 \gcd(a,p_i^{c_i})=1 gcd(a,pici)=1,那么显然成立(根据欧拉定理)。
否则, a a a 一定是 p i p_i pi 的倍数,即可以分解成 x × p i x\times p_i x×pi,其中 x x x 不一定与 p i p_i pi 互质,但是这不重要。
引理: φ ( p i c i ) ≥ c i \varphi(p_i^{c_i})\geq c_i φ(pici)≥ci。
证明: 由于 φ ( p i c i ) = ( p i − 1 ) × p i c i − 1 \varphi(p_i^{c_i})=(p_i-1)\times p_i^{c_i-1} φ(pici)=(pi−1)×pici−1,所以上式等价于 ( p i − 1 ) × p i c i − 1 ≥ c i (p_i-1)\times p_i^{c_i-1}\geq c_i (pi−1)×pici−1≥ci。
考虑 p i = 2 p_i=2 pi=2 的情况,手玩发现 c i = 1 c_i=1 ci=1 时是满足的,归纳一下 c i > 1 c_i>1 ci>1 时也满足。然后再归纳一下,由于 p i p_i pi 增大时不等式左边增大,右边不变,所以 p i > 2 p_i>2 pi>2 时也满足,证毕。
由于 φ \varphi φ 是积性函数,根据引理,有 c ≥ φ ( p ) ≥ φ ( p i c i ) ≥ c i c\geq \varphi(p)\geq \varphi(p_i^{c_i})\geq c_i c≥φ(p)≥φ(pici)≥ci。
又由于 a c ≡ x c p i c a^c\equiv x^cp_i^c ac≡xcpic,所以 p i c i ∣ p i c ∣ a c p_i^{c_i}|p_i^c|a^c pici∣pic∣ac,即 a c ≡ 0 ( m o d p i c i ) a^c\equiv 0\pmod {p_i^{c_i}} ac≡0(modpici)。
而 φ ( p ) \varphi(p) φ(p) 也是大于等于 c i c_i ci 的,即 a φ ( p ) ≡ 0 ( m o d p i c i ) a^{\varphi(p)}\equiv 0\pmod {p_i^{c_i}} aφ(p)≡0(modpici)。
所以有 a c ≡ a c m o d φ ( p ) + φ ( p ) ≡ 0 ( m o d p i c i ) a^c\equiv a^{c\bmod \varphi(p)+\varphi(p)}\equiv 0\pmod {p_i^{c_i}} ac≡acmodφ(p)+φ(p)≡0(modpici)。证毕。
线性方程
定义: 未知数的次数都是 1 1 1 次的方程。
如: 2 x + 3 y + z − 4 = 9 2x+3y+z-4=9 2x+3y+z−4=9。
假如线性方程中含有 n n n 个未知数,那么至少需要 n n n 条包含这 n n n 个未知数的线性方程才能求出这些未知数。
当然,也会有
n
n
n 条线性方程也求不出
n
n
n 个未知数的情况,比如说当
n
n
n 等于
2
2
2 时,给你这样两个方程:
{
3
x
+
6
y
=
6
1
x
+
2
y
=
2
\begin{cases} 3x+6y=6\\ 1x+2y=2 \end{cases}
{3x+6y=61x+2y=2
显然求不出来(指无限解)。
高斯消元
这是一个用来求解线性方程组的算法。
质数
定义: 对于一个数 x x x,假如他只有 1 1 1 和它本身两个因子,那么称这个数为质数。
别名: 素数。
判断是否为质数
暴力
尝试去找这个数的因子。
这个有个优化,我们知道,假如一个数 n n n 拥有因子 a a a,那么他肯定也拥有另一个因子 n a \dfrac n a an,而 a a a 和 n a \frac n a an 这两个数之间至少有一个小于等于 n \sqrt n n,所以我们只需要在 2 2 2 ~ n \sqrt n n 这个区间内去找有没有因子即可。
代码:
bool prime(int x)
{
if(x==1)return false;//特判掉1
for(int i=2;i*i<=x;i++)//枚举2~sqrt(x)
if(x%i==0)return false;//假如有因子,那么就不是质数
return true;//假如没有找到因子,那么就是质数
}
费马小定理
我们知道,如果有 p ∈ p r i m e p\in prime p∈prime,那么对于任意 a ∈ Z ∗ a\in Z^* a∈Z∗,都有 a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv 1 \pmod p ap−1≡1(modp)。
那么对于一个数 x x x,我们随便找几个与 x x x 互质的数,看他们的 x − 1 x-1 x−1 次方是否为 1 1 1 即可,假如有一个数的 x − 1 x-1 x−1 次方不为 1 1 1,那么 x x x 就不是质数。
但是这是一个看人品的算法,因为有一种叫伪素数的东西。
这种东西他不是素数,但是它可以通过这种费马小定理探测法。伪素数表网上一搜一大堆,这里不列举了。
Miller-Rabin 算法
这虽然也是一个看人品的算法,但是比费马小定理探测法可靠的多。
二次探测定理
具体内容: 假如 p p p 是个质数,那么对于同余方程 x 2 ≡ 1 ( m o d p ) x^2 \equiv 1 \pmod p x2≡1(modp),它的解只有 x 1 = 1 , x 2 = p − 1 x_1=1,x_2=p-1 x1=1,x2=p−1。
证明: 解一下就好了。
x
2
≡
1
(
m
o
d
p
)
x
2
−
1
≡
0
(
m
o
d
p
)
(
x
+
1
)
(
x
−
1
)
≡
0
(
m
o
d
p
)
\begin{aligned} x^2&\equiv 1 \pmod p\\ x^2-1&\equiv 0 \pmod p\\ (x+1)(x-1)&\equiv 0 \pmod p\\ \end{aligned}
x2x2−1(x+1)(x−1)≡1(modp)≡0(modp)≡0(modp)
所以有 x + 1 ≡ 0 ( m o d p ) x+1\equiv 0 \pmod p x+1≡0(modp) 或 x − 1 ≡ 0 ( m o d p ) x-1\equiv 0 \pmod p x−1≡0(modp),解出来就是 x ≡ 1 o r p − 1 ( m o d p ) x\equiv 1~or~p-1 \pmod p x≡1 or p−1(modp)。
回到 Miller-Rabin 算法。
算法流程如下:
- 随手选出一个与 x x x 互质的数 a a a。
- 用费马小定理探测法判断这个 a a a,假如满足 a x − 1 ≡ 1 ( m o d x ) a^{x-1}\equiv 1 \pmod x ax−1≡1(modx),进入下一步,否则可以判断出 x x x 不为素数。
- 因为 a x − 1 ≡ 1 ( m o d x ) a^{x-1}\equiv 1 \pmod x ax−1≡1(modx),根据二次探测定理,假如 x x x 是个质数,那么有 a x − 1 2 ≡ 1 o r p − 1 ( m o d x ) a^{\frac {x-1} 2} \equiv 1~or~p-1 \pmod x a2x−1≡1 or p−1(modx),假如 a x − 1 2 a^{\frac {x-1} 2} a2x−1 不是 1 1 1 或 p − 1 p-1 p−1,那么 x x x 不为质数。假如 a x − 1 2 a^{\frac {x-1} 2} a2x−1 为 1 1 1 并且 x − 1 2 \frac {x-1} 2 2x−1 是个偶数,那么就继续探测 x − 1 4 \frac {x-1} 4 4x−1,否则停下。
根据奇奇怪怪的证明,假如一个数 x x x 成功通过了一次 Miller-Rabin 算法,那么它不是质数的可能性降到 1 4 \frac 1 4 41,那么不妨多测几次,假如测 y y y 次,那么它不是质数的可能性就就降到了 1 4 y \frac 1 {4^y} 4y1。(证明?我怎么可能会。)
素数筛法
暴力筛
对于 1 1 1 ~ n n n 的每个数,都判断一下是不是质数。为了保证正确性以及为了方便,采取暴力判断的方式。
代码:
#include <cstdio>
bool prime(int x)
{
if(x==1)return false;
for(int i=2;i*i<=x;i++)
if(x%i==0)return false;
return true;
}
int main()
{
for(int i=1;i<=100;i++)
if(prime(i))printf("%d ",i);
}
时间复杂度: O ( n n ) O(n\sqrt n) O(nn)
优化的暴力筛
对于任意一个数 x x x,都可以表示成 6 n − 5 , 6 n − 4 , 6 n − 3 , 6 n − 2 , 6 n − 1 , 6 n 6n-5,6n-4,6n-3,6n-2,6n-1,6n 6n−5,6n−4,6n−3,6n−2,6n−1,6n 中的一种,比如说 7 = 2 × 6 − 5 , 21 = 4 × 6 − 3 7=2\times 6 -5,21=4 \times 6 -3 7=2×6−5,21=4×6−3。
我们发现,假如一个数可以表示成 6 n − 4 , 6 n − 2 6n-4,6n-2 6n−4,6n−2,那么他一定是 2 2 2 的倍数,不可能是质数。
假如一个数可以表示成 6 n − 3 6n-3 6n−3,那么他一定是 3 3 3 的倍数,不可能是质数。
假如一个数可以表示成· 6 n 6n 6n,那么他一定是 6 6 6 的倍数,不可能是质数。
所以,质数都可以表示成 6 n − 5 6n-5 6n−5 或 6 n − 1 6n-1 6n−1。
那么尝试优化上面的暴力筛:对于一个数 x x x,我们找他的因子的时候,只找质因子,这样的效果是相同的,然后枚举质因子的时候就可以 6 6 6 个 6 6 6 个跳,而不是 1 1 1 个 1 1 1 个跳了。
时间复杂度: O ( n 6 ) O(\frac {\sqrt n} 6) O(6n)。
埃氏筛
我们知道,对于一个质数 x x x,它的倍数 2 x , 3 x , 4 x , . . . 2x,3x,4x,... 2x,3x,4x,... 肯定都不是质数。
埃氏筛就是用了这样的搞法。
代码:
#include <cstdio>
bool v[110];//记录每个数是否为素数,false为是,true为不是
int main()
{
for(int i=2;i<=100;i++)
if(!v[i])
{
printf("%d ",i);
for(int j=2;i*j<=100;j++)
v[i*j]=true;
}
}
时间复杂度: O ( n ln n ) O(n\ln n) O(nlnn)。
证明的话参考调和级数即可。
欧拉筛
埃氏筛还不够优秀,于是欧拉筛出现了。
先贴代码:
#include <cstdio>
bool v[110];
int prime[110],t=0;
int main()
{
for(int i=2;i<=100;i++)
{
if(!v[i])prime[++t]=i;//假如i没有被筛到过,那么记录下来
for(int j=1;j<=t&&i*prime[j]<=100;j++)
//枚举一个素数prime[j],筛掉i*prime[j]这个数
{
v[i*prime[j]]=true;
if(i%prime[j]==0)break;//假如prime[j]是i的因子,那么就停下
//这一步下面会详细讲
}
}
for(int i=1;i<=t;i++)
printf("%d ",prime[i]);
}
欧拉筛最神的就是这一行:
if(i%prime[j]==0)break;
这样可以保证:对于每个合数,他都只会被自己最小的那个质因子筛掉。
简单的证明:
比如说对于一个合数 n n n,假设它最小的质因子是 x x x,以及他还有另外一个质因子 y y y。显然满足 x < y x<y x<y。
要筛掉 n n n,有两种情况:
- 当 i i i 枚举到 n x \frac n x xn 时, p r i m e [ j ] prime[j] prime[j] 枚举到 x x x
- 当 i i i 枚举到 n y \frac n y yn 时, p r i m e [ j ] prime[j] prime[j] 枚举到 y y y
有一个性质: x x x 一定是 n y \frac n y yn 的因子。
那么当 i i i 枚举到 n y \frac n y yn 时, p r i m e [ j ] prime[j] prime[j] 在枚举到 x x x 时就会停下,不会继续枚举到 y y y。
因此,第一种情况不可能出现。所以,每个合数只可能被自己最小的质因子筛掉。
于是,时间复杂度: O ( n ) O(n) O(n)。
唯一分解定理
对于任意一个正整数 n n n,都可以被唯一地拆成若干个质数相乘的形式,形如: p 1 a 1 p 2 a 2 . . . p m a m p_1^{a_1}p_2^{a_2}...p_m^{a_m} p1a1p2a2...pmam,比如说, 60 = 2 2 × 3 1 × 5 1 60=2^2\times 3^1 \times 5^1 60=22×31×51。
这个唯一性过于显然,没有必要证明了。
求因子数
对于一个可以分解为 p 1 a 1 p 2 a 2 . . . p m a m p_1^{a_1}p_2^{a_2}...p_m^{a_m} p1a1p2a2...pmam 的数 n n n,它的因子肯定也可以由 p 1 , p 2 , . . . , p m p_1,p_2,...,p_m p1,p2,...,pm 的若干次方相乘得到,并且对于任意一个因子的一个质因数 p i p_i pi,它在因子中的指数一定不大于在 n n n 中的指数。
那么对于 p i p_i pi,它的指数的取值范围就是 [ 0 , a i ] [0,a_i] [0,ai],所以 n n n 的因子数就是 ( a 1 + 1 ) ( a 2 + 1 ) . . . ( a m + 1 ) (a_1+1)(a_2+1)...(a_m+1) (a1+1)(a2+1)...(am+1)。
求因子之和
考虑计算每一个质因子的贡献,用分配率加起来,那么就是:
(
p
1
0
+
p
1
1
+
.
.
.
+
p
1
a
m
)
(
p
2
0
+
p
2
1
+
.
.
.
+
p
2
a
m
)
.
.
.
(
p
m
0
+
p
1
m
+
.
.
.
+
p
m
a
m
)
(p_1^0+p_1^1+...+p_1^{a_m})(p_2^0+p_2^1+...+p_2^{a_m})...(p_m^0+p_1^m+...+p_m^{a_m})
(p10+p11+...+p1am)(p20+p21+...+p2am)...(pm0+p1m+...+pmam)
中国剩余定理
扩展中国剩余定理
容斥
这是个极大的版块,不太能讲完,这里就浅浅涉及一些。
据我所知,容斥大概有两种形式。
形式1
定义一个函数
g
(
S
)
g(S)
g(S) 表示集合
S
S
S 的价值,定义子集和函数
f
(
S
)
=
∑
T
∈
S
g
(
T
)
f(S)=\sum_{T\in S} g(T)
f(S)=∑T∈Sg(T),假如我们知道了
f
f
f,可以通过这个柿子得到
g
g
g:
g
(
S
)
=
∑
S
∈
T
(
−
1
)
∣
S
∣
−
∣
T
∣
f
(
T
)
g(S)=\sum_{S\in T}(-1)^{|S|-|T|}f(T)
g(S)=S∈T∑(−1)∣S∣−∣T∣f(T)
类似的,可以定义超集和函数
f
(
S
)
=
∑
S
∈
T
g
(
T
)
f(S)=\sum_{S\in T}g(T)
f(S)=∑S∈Tg(T),知道
f
f
f 时就可以通过类似的柿子得到
g
g
g:
g
(
S
)
=
∑
T
∈
S
(
−
1
)
∣
S
∣
−
∣
T
∣
f
(
T
)
g(S)=\sum_{T\in S}(-1)^{|S|-|T|}f(T)
g(S)=T∈S∑(−1)∣S∣−∣T∣f(T)
很多时候,这个集合 S S S 里面记录的是限制,比如 f ( S ) f(S) f(S) 表示至少满足 S S S 这些限制的情况数量,则 g ( S ) g(S) g(S) 表示恰好满足 S S S 这些限制的情况数量。
至少
这个限制大多数时候是比恰好
要松的,往往会更好求,此时就可以通过求出
f
f
f,然后容斥再求出
g
g
g。
形式2
记所有组合对象为
U
U
U,有若干种属性
1
,
2
,
⋯
,
k
1,2,\cdots,k
1,2,⋯,k,每个组合对象拥有一些属性,记
A
i
A_i
Ai 表示所有拥有属性
P
i
P_i
Pi 的对象集合,令
S
S
S 为包含了任意一些属性的集合,那么有:
∣
⋃
i
∈
S
A
i
∣
=
∑
T
∈
S
(
−
1
)
∣
T
∣
−
1
∣
⋂
j
∈
T
A
j
∣
|\bigcup_{i\in S}A_i|=\sum_{T\in S}(-1)^{|T|-1} |\bigcap_{j\in T} A_j|
∣i∈S⋃Ai∣=T∈S∑(−1)∣T∣−1∣j∈T⋂Aj∣
显然也可以将并集和交集符号交换一下得到类似的容斥公式。
以这个柿子为例,一个小学就学过的简单容斥问题其实就是它的经典应用:
a a a 个人学FFT, b b b 个人学NTT, c c c 个人学FMT, d d d 个人同时学FFT和NTT, e e e 个人同时学NTT和FMT, f f f 个人同时学FFT和FMT, g g g 个人同时学FFT、NTT和FMT,求有多少个人学了至少一种算法。
众所周知答案是 a + b + c − d − e − f + g a+b+c-d-e-f+g a+b+c−d−e−f+g,本质上容斥的过程是:先把所有人算上,然后把重复计算的减掉,再把重复减掉的加上,如此反复直到最后一次计算后没有重复的为止。
看起来这样可以使每个人只被统计一次,如何用数学方法严谨证明一下呢?
对于一个学习了
n
n
n 个算法的人,他会被统计这么多次:
∑
i
=
1
n
(
n
i
)
(
−
1
)
i
−
1
\sum_{i=1}^n \binom n i (-1)^{i-1}
i=1∑n(in)(−1)i−1
其中,假设他学习的算法集合为 S S S,那么它就有 ( n i ) \binom n i (in) 个大小为 i i i 的子集 T T T,贡献的系数为 ( − 1 ) ∣ T ∣ − 1 (-1)^{|T|-1} (−1)∣T∣−1,即 ( − 1 ) i − 1 (-1)^{i-1} (−1)i−1。
推一推:
=
−
(
∑
i
=
0
n
(
n
i
)
(
−
1
)
i
)
+
1
=
(
1
−
1
)
n
+
1
=
1
=-\left(\sum_{i=0}^n \binom n i(-1)^i\right)+1=(1-1)^n+1=1
=−(i=0∑n(in)(−1)i)+1=(1−1)n+1=1
这样就证完了,中间用到的是二项式定理。
简单反演
对于一个关系: f n = ∑ a n , i g ( i ) f_n=\sum a_{n,i}g(i) fn=∑an,ig(i),知道 f f f 求回 g g g 的过程便是反演。
似乎大部分反演的本质就是容斥。
莫比乌斯反演
当时相当的菜,只会背个定义套套式子,这里写一些其他的东西。
莫比乌斯函数
μ
\mu
μ 是个相当厉害的容斥系数,还记得在欧拉函数那里看到的一条式子吗?
φ
(
n
)
=
∑
i
∣
n
μ
(
i
)
n
i
\varphi(n)=\sum_{i|n} \mu(i)\frac n i
φ(n)=i∣n∑μ(i)in
就以这个为例子,来初步理解一下 μ \mu μ 的容斥意义。
姑且先看看
φ
(
n
)
\varphi(n)
φ(n) 的本质:
n
n
n 以内与
n
n
n 互质的数的个数。也就有:
φ
(
n
)
=
∑
i
=
1
n
[
gcd
(
i
,
n
)
=
1
]
=
∑
i
=
1
n
∑
d
∣
gcd
(
i
,
n
)
μ
(
d
)
\begin{aligned} \varphi(n)&=\sum_{i=1}^n [\gcd(i,n)=1]\\ &=\sum_{i=1}^n\sum_{d|\gcd(i,n)}\mu(d) \end{aligned}
φ(n)=i=1∑n[gcd(i,n)=1]=i=1∑nd∣gcd(i,n)∑μ(d)
注意到
d
∣
gcd
(
i
,
n
)
d|\gcd(i,n)
d∣gcd(i,n) 可以拆成
d
∣
i
&
d
∣
n
d|i~\&~d|n
d∣i & d∣n,带进去会变得很方便:
φ
(
n
)
=
∑
i
=
1
n
∑
d
∣
n
μ
(
d
)
[
d
∣
i
]
=
∑
d
∣
n
μ
(
d
)
∑
i
=
1
n
[
d
∣
i
]
=
∑
d
∣
n
μ
(
d
)
n
i
\begin{aligned} \varphi(n)&=\sum_{i=1}^n\sum_{d|n}\mu(d)[d|i]\\ &=\sum_{d|n}\mu(d)\sum_{i=1}^n [d|i]\\ &=\sum_{d|n}\mu(d)\frac n i \end{aligned}
φ(n)=i=1∑nd∣n∑μ(d)[d∣i]=d∣n∑μ(d)i=1∑n[d∣i]=d∣n∑μ(d)in
这样就简单证明完了,但是如何从容斥方面来考虑这个柿子的正确性呢?
回顾上面所说:容斥是个反复加减的过程;函数 f ( S ) f(S) f(S) 中的 S S S 常常用来表示限制的集合。
注意这里有什么限制:假设 n n n 可以分解为 p 1 c 1 p 2 c 2 . . . p k c k p_1^{c_1}p_2^{c_2}...p_k^{c_k} p1c1p2c2...pkck,那么当且仅当 i i i 不是任何一个 p j p_j pj 的倍数时,它才与 n n n 互质。这个可以分解成 k k k 条限制,第 j j j 条为 i i i 需要与 p j p_j pj 互质。
考虑反复加减:看一个比较小的例子,当 n = 6 n=6 n=6 时,考虑没有限制时,先加上所有数,即 1 1 1 ~ 6 6 6,然后由于 6 = 2 1 × 3 1 6=2^1\times 3^1 6=21×31,所以我们需要减去 2 2 2 和 3 3 3 的倍数,注意到这样的话 6 6 6 的倍数会被减去两次,我们需要加回来,所以再加上 6 6 6 的倍数,答案为 6 − 3 − 2 + 1 = 2 6-3-2+1=2 6−3−2+1=2。
看到这里,是不是已经有熟悉的感觉了?这和上面那个反复加减的例子本质几乎一致!
然后你再回顾 μ \mu μ 的定义:假如 i i i 可以分解为 k k k 个互不相同的质因子,那么 μ ( k ) = ( − 1 ) k \mu(k)=(-1)^k μ(k)=(−1)k,这不就正好表示, i i i 的倍数在容斥过程中应该加上还是减去吗!
而当 i i i 拥有平方因子时, i i i 对容斥是没有贡献的,所以 μ ( i ) \mu(i) μ(i) 等于 0 0 0。
是不是一切都顺理成章了起来,当初 μ \mu μ 那个奇怪的定义也显得理所当然。
这就是容斥之美,数学的本质往往是很简单的。