素数,约数,同余。
基础数论
常见的思维方法有:观察与分析、概括与抽象、分析与综合、特殊与一般、类比、归纳和演绎等;
常见的数学思想有:函数与方程思想、数形结合思想、分类讨论思想、转化(化归)思想等。
之所以先介绍这些,是因为这些东西确实很重要,在算法竞赛中蕴含得特别多,最常见的就是分类讨论,其他思想其实也特别多。
素数
素数的判定
枚举 2 2 2 到 n \sqrt n n 的数暴力判定。
素数筛
主要掌握埃氏筛和线性筛,并理解二者的原理,会魔改。
素因数分解
单个数素因数分解
对单个数的素因数分解,只需要从 2 2 2 枚举到 n \sqrt n n ,对于每次枚举到的约数,不断让 n n n 除之,统计除法次数,直到除不开。最后如果剩下的部分大于 1 1 1 ,则剩下的部分也是一个素数。可以证明,这样做下去我们只有在枚举到质数的时候才会真正做除法。
阶乘素因数分解
本需求会在高精度组合数 计算中被用到。
分别对每个数进行分解的复杂度是 O ( n n ) O(n\sqrt n) O ( n n ) ,数据范围比较大的时候难以承受。
另一种方法是先筛出 n n n 范围内的所有素数,然后枚举素数,统计每个素数的贡献,即直接统计每个素数在阶乘中出出现了多少次,手段是不断除以这个素数p p p ,第k k k 次做除法得到的商,就代表有多少1 1 1 到 n n n 之间的数含有p p p 的最高次数是p k p^k p k 。这种方法需要O ( n ) O(n) O ( n ) 预处理素数,然后O ( ∑ log p n ) O(\sum \log_pn) O ( ∑ log p n ) 的时间统计所有素数的贡献。考虑到 1 1 1 到 n n n 的素数个数大约是O ( n ln n ) O(\frac{n}{\ln n}) O ( ln n n ) 级别的,所以做放缩,知∑ log p n ≤ n ln n log 2 n \sum \log_p n \leq \frac{n}{\ln n} \log_2 n ∑ log p n ≤ ln n n log 2 n ,利用换底公式可知右式的渐进上界是 O ( n ) O(n) O ( n ) 。
参考代码如下:
// flag = 1 或 -1
void split(int n, int flag) {
for (int i = 0; i < cnt; i++) {
int p = prime[i];
while (p <= n) {
primeidx[i] += (flag * (n / p));
p = p * prime[i];
}
}
}
约数
约数之和
求一系列数的乘积所得到的数的所有的约数之和。
先对每个数做素因数分解,然后使用生成函数的思想,可知最后的结果为
a n s = ∏ i = 1 k ∑ j = 0 α i p i j ans = \prod_{i = 1}^{k}\sum_{j = 0}^{\alpha_i}p_{i}^j
a n s = i = 1 ∏ k j = 0 ∑ α i p i j
根据数据范围,选择使用秦九韶算法,分治或者等比数列求和公式进行计算即可。
最大公约数
辗转相除
最常用的方法。
更相减损
弱化版的辗转相除。
高精度gcd
高精度gcd常用更相减损术去做,因为高精度的取模不好写。
互素与欧拉函数
欧拉函数的定义是:ϕ ( x ) \phi(x) ϕ ( x ) 定义为 1 1 1 到 x x x 中与 x x x 互素的数的个数。
容易发现,对素数 p p p 来说,ϕ ( p ) = p − 1 \phi(p) = p - 1 ϕ ( p ) = p − 1 。
ϕ ( x ) \phi(x) ϕ ( x ) 的计算公式容易理解:
ϕ ( x ) = x × ( 1 − 1 p 1 ) × ( 1 − 1 p 2 ) . . . × ( 1 − 1 p k ) \phi(x) = x \times(1 - \frac{1}{p_1})\times(1 - \frac{1}{p_2})...\times(1 - \frac{1}{p_k})
ϕ ( x ) = x × ( 1 − p 1 1 ) × ( 1 − p 2 1 ) . . . × ( 1 − p k 1 )
可以从概率的角度/容斥原理的角度去考虑这个公式(有多大概率取到不是p i p_i p i 的倍数的数)。
考虑 c = a × b c = a\times b c = a × b ,如果有 gcd ( a , b ) = 1 \gcd(a, b) = 1 g cd( a , b ) = 1 ,则由上述计算公式中,可以发现 ϕ ( c ) = ϕ ( a ) × ϕ ( b ) \phi(c) = \phi(a)\times \phi(b) ϕ ( c ) = ϕ ( a ) × ϕ ( b ) (因为没有相同素因子)
如果 gcd ( a , b ) ≠ 1 \gcd(a,b) \ne 1 g cd( a , b ) = 1 ,倘若 a a a 为素数,那么也是有很好的性质的。因为 a a a 为素数,且 gcd ( a , b ) ≠ 1 \gcd(a, b) \neq 1 g cd( a , b ) = 1 ,所以 gcd ( a , b ) = a \gcd(a, b) = a g cd( a , b ) = a ,考虑到 ϕ ( a ) \phi(a) ϕ ( a ) 和 ϕ ( b ) \phi(b) ϕ ( b ) 的计算式,可以发现 ϕ ( c ) = a × ϕ ( b ) \phi(c) = a \times \phi(b) ϕ ( c ) = a × ϕ ( b ) 。
利用线性筛,可以 O ( n ) O(n) O ( n ) 的时间预处理出 ϕ ( x ) \phi(x) ϕ ( x ) 。
同余
贝祖定理
若 gcd ( a , b ) = d \gcd(a, b) = d g cd( a , b ) = d ,那么存在整数 x , y x, y x , y ,使得 a x + b y = d ax + by = d a x + b y = d 成立。
显然,若 d d d 不能整除 c c c ,那么 a x + b y = c ax + by = c a x + b y = c 一定没有整数解。
特殊地,gcd ( a , b ) = 1 \gcd(a, b) = 1 g cd( a , b ) = 1 时,存在整数 x , y x, y x , y ,使得 a x + b y = 1 ax + by = 1 a x + b y = 1 成立。
中国剩余定理
普通CRT
对于同余方程组
{ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) . . . x ≡ a n ( m o d m n ) \begin{cases}
x\equiv a_1(mod\ m_1)\\
x\equiv a_2(mod\ m_2)\\
...\\
x\equiv a_n(mod\ m_n)\\
\end{cases}
⎩ ⎪ ⎪ ⎪ ⎨ ⎪ ⎪ ⎪ ⎧ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) . . . x ≡ a n ( m o d m n )
其中gcd ( m i , m j ) = 1 \gcd(m_i, m_j) = 1 g cd( m i , m j ) = 1 ,1 ≤ i , j ≤ n , i ≠ j 1\leq i, j\leq n,i \neq j 1 ≤ i , j ≤ n , i = j
设M = ∏ i = 1 n m i M = \prod\limits_{i = 1}^nm_i M = i = 1 ∏ n m i ,M i = M m i M_i = \frac{M}{m_i} M i = m i M ,M i − 1 M i ≡ 1 ( m o d m i ) M_i^{-1}M_i\equiv1(mod\ m_i) M i − 1 M i ≡ 1 ( m o d m i )
则 x ≡ ∑ i = 1 n M i − 1 M i b i ( m o d m j ) , ∀ 1 ≤ j ≤ n x\equiv\sum\limits_{i= 1}^{n}M_i^{-1}M_ib_i(mod\ m_j),\forall 1\leq j\leq n x ≡ i = 1 ∑ n M i − 1 M i b i ( m o d m j ) , ∀ 1 ≤ j ≤ n
即我们给出了一个构造 $x $ 的方式。容易验证上式成立。
在求M i − 1 M_i^{-1} M i − 1 时,注意到gcd ( M i − 1 , m i ) = 1 \gcd(M_i^{-1}, m_i) = 1 g cd( M i − 1 , m i ) = 1 ,所以可以使用 exgcd
进行求解,而使用其他的方法求可能会求不出来(exgcd
只要有数和模数互素即可,而其他的方法需要模数是素数)。
exCRT
对于上述同余方程组,当不满足模数两两互素的时候,要想构造出 x x x ,就需要使用扩展中国剩余定理了。
扩展中国剩余定理的思路是逐步两两合并方程:
{ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) \begin{cases}
x\equiv a_1(mod\ m_1)\\
x\equiv a_2(mod\ m_2)
\end{cases}
{ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 )
改写成等式:
{ x = m 1 p + a 1 x = m 2 q + a 2 \begin{cases}
x = m_1p+a_1\\
x = m_2q+a_2
\end{cases}
{ x = m 1 p + a 1 x = m 2 q + a 2
需要有合适的整数使得p , q p, q p , q 都是整数,所以 m 1 p − m 2 q = a 2 − a 1 m_1p - m_2q = a_2 - a_1 m 1 p − m 2 q = a 2 − a 1 要有整数解,所以gcd ( m 1 , m 2 ) ∣ ( a 2 − a 1 ) \gcd(m_1, m_2)\mid(a_2 - a_1) g cd( m 1 , m 2 ) ∣ ( a 2 − a 1 ) 才能保证有合适的 x x x 。
费马小定理
主要用来求解模数是质数时的逆元。
如果 gcd ( a , p ) = 1 \gcd(a,p) = 1 g cd( a , p ) = 1 ,那么 a p ≡ a ( m o d p ) a^p \equiv a(mod\ p) a p ≡ a ( m o d p ) ,特别的,当 p p p 为质数时,有 a p − 1 ≡ 1 ( m o d p ) a^{p - 1}\equiv1(mod\ p) a p − 1 ≡ 1 ( m o d p )
哥德巴赫猜想
TODO
虽然是猜想,但是在很大的范围内验证是对的,所以有时候会用来做一些判断。
exgcd
用于求解二元一次方程的整数解,很多时候是求解包含逆元未知数的二元一次方程。
exgcd
的原理是,考虑辗转相除的过程,gcd(a, b) = gcd(b, a % b), b != 0
不妨设 gcd ( a , b ) = d \gcd(a, b) = d g cd( a , b ) = d ,第 i i i 次迭代时得到的方程是 a i x + b i y = d a_ix + b_iy = d a i x + b i y = d ,则有:
a n x + b n y = a n + 1 x + b n + 1 y = d a_nx + b_ny = a_{n + 1}x + b_{n + 1}y = d
a n x + b n y = a n + 1 x + b n + 1 y = d
进一步,使用 gcd ( a , b ) = gcd ( b , a % b ) \gcd(a, b) = \gcd(b, a \% b) g cd( a , b ) = g cd( b , a % b ) (在没有到递归边界的时候),有:
a n x + b n y = b n x + ( a n % b n ) y = d a_nx + b_ny = b_nx + (a_n \% b_n)y = d
a n x + b n y = b n x + ( a n % b n ) y = d
利用等式 a % b = a − [ a b ] b a \% b = a -[ \frac{a}{b}]b a % b = a − [ b a ] b ,代换之后有:
a n x + b n y = b n x + ( a n − [ a n b n ] b n ) y = d a_nx + b_ny = b_nx + (a_n -[ \frac{a_n}{b_n}]b_n)y = d
a n x + b n y = b n x + ( a n − [ b n a n ] b n ) y = d
线性同余方程
形如 a x ≡ b ( m o d m ) ax\equiv b(mod\ m) a x ≡ b ( m o d m ) ,可以转化为 a x + m y = b ax + my = b a x + m y = b ,根据贝祖定理,有整数解的充要条件是 gcd ( a , m ) ∣ b \gcd(a, m) \mid b g cd( a , m ) ∣ b
高次同余方程