带余除法#
设 a , b a,b a , b 是整数,a ≠ 0 a\not= 0 a = 0 ,如果存在 q q q 是整数,使得 b = a q b=aq b = a q ,那么有 b b b 可被 a a a 整除,记作 a ∣ b a \mid b a ∣ b ,且称 b b b 是 a a a 的倍数 ,a a a 是 b b b 的因数 。
若 a ∣ b a \mid b a ∣ b 且 b ∣ c b \mid c b ∣ c ,则 ∀ x , y \forall x,y ∀ x , y ,有a ∣ x b + y c a \mid xb+yc a ∣ x b + yc .
若 a ∣ b a \mid b a ∣ b 且 b ∣ c b \mid c b ∣ c ,则 a ∣ c a \mid c a ∣ c .
设 m ≠ 0 m\neq 0 m = 0 ,则 a ∣ b a\mid b a ∣ b ,当且仅当 m a ∣ m b ma\mid mb ma ∣ mb .
若 a ∣ b a\mid b a ∣ b 且 b ∣ a b\mid a b ∣ a ,则 a = ± b a=\pm b a = ± b .
若 a ∣ b a \mid b a ∣ b 且 b ≠ 0 b\not= 0 b = 0 ,则 ∣ a ∣ ≤ ∣ b ∣ |a|\leq |b| ∣ a ∣ ≤ ∣ b ∣ .
带余除法#
设 a , b a,b a , b 是正整数,且 b ≠ 0 b\not= 0 b = 0 ,则存在唯一整数 q , r q, r q , r 使得 a = q b + r , 0 ≤ r < ∣ b ∣ a = qb+r,0\leq r < |b| a = q b + r , 0 ≤ r < ∣ b ∣ .
这个式子叫做带余除法,记余数 r = a m o d b r=a\bmod b r = a mod b 。当 r = 0 r=0 r = 0 时,b b b 是 a a a 的因数。
( a ± b ) m o d p = ( a m o d p ± b m o d p ) m o d p (a\pm b)\bmod p=(a\bmod p\pm b\bmod p)\bmod p ( a ± b ) mod p = ( a mod p ± b mod p ) mod p
( a × b ) m o d p = ( a m o d p × b m o d p ) m o d p (a\times b)\bmod p=(a\bmod p\times b\bmod p)\bmod p ( a × b ) mod p = ( a mod p × b mod p ) mod p
最大公因数与最小公倍数#
最大公因数#
设 a a a 和 b b b 是两个非零整数,如果 d ∣ a d \mid a d ∣ a 且 d ∣ b d \mid b d ∣ b ,则称 d d d 是 a a a 与 b b b 的公因数 。
所有公因数中最大的被称为 a , b a,b a , b 的最大公因数 ,记为 gcd ( a , b ) \gcd(a,b) g cd( a , b ) .
特殊地,当 gcd ( a , b ) = 1 \gcd(a,b)=1 g cd( a , b ) = 1 时,称 a , b a,b a , b 互质 。
最小公倍数#
设 a a a 和 b b b 是两个非零整数,如果 a ∣ d a \mid d a ∣ d 且 a ∣ d a \mid d a ∣ d ,则称 d d d 是 a a a 与 b b b 的公倍数 。
所有公倍数中最小的被称为 a , b a,b a , b 的最小公倍数 ,记做 lcm ( a , b ) \operatorname{lcm}(a,b) lcm ( a , b ) .
若 a ∣ m , b ∣ m a \mid m,b \mid m a ∣ m , b ∣ m ,则gcd ( a , b ) ∣ m \gcd(a,b) \mid m g cd( a , b ) ∣ m .
若 d ∣ a , d ∣ b d \mid a,d \mid b d ∣ a , d ∣ b ,则 d ∣ lcm ( a , b ) d \mid \operatorname{lcm}(a,b) d ∣ lcm ( a , b ) .
lcm ( a , b ) = a b gcd ( a , b ) \operatorname{lcm}(a,b)=\dfrac{ab}{\gcd(a,b)} lcm ( a , b ) = g cd( a , b ) ab .
设 m , a , b m,a,b m , a , b 是正整数,则 lcm ( m a , m b ) = m × lcm ( a , b ) \operatorname{lcm}(ma, mb)=m\times \operatorname{lcm}(a, b) lcm ( ma , mb ) = m × lcm ( a , b ) .
若 m m m 是非零整数 a 1 , a 2 , a 3 , ⋅ ⋅ ⋅ , a n a_1,a_2,a_3,\cdot\cdot\cdot, a_n a 1 , a 2 , a 3 , ⋅ ⋅ ⋅ , a n 的公倍数,则 lcm ( a 1 , a 2 , ⋅ ⋅ ⋅ , a n ) ∣ m \operatorname{lcm}(a_1, a_2,\cdot\cdot\cdot\;, a_n) \mid m lcm ( a 1 , a 2 , ⋅ ⋅ ⋅ , a n ) ∣ m .
欧几里得算法#
欧几里得算法,又名辗转相除法 。
有结论:
gcd ( a , b ) = gcd ( b , a m o d b ) \gcd(a,b)=\gcd(b,a\bmod b) g cd( a , b ) = g cd( b , a mod b )
设 a = q b + r , ( r < q ) a=qb+r,(r<q) a = q b + r , ( r < q ) ,d ∣ a d\mid a d ∣ a 且 d ∣ b d\mid b d ∣ b .
由于 a = q b + r a=qb+r a = q b + r ,故 r = a − q b r=a-qb r = a − q b ,
另一方面,由于 d ∣ a d\mid a d ∣ a 且 d ∣ b d\mid b d ∣ b ,有 d ∣ a − q b d\mid a-qb d ∣ a − q b .
于是得到 d ∣ r d\mid r d ∣ r ,所以任意 a , b a,b a , b 的公因数都是 b , r b,r b , r 的公因数,r < a r<a r < a ,所以 gcd ( a , b ) = gcd ( b , r ) \gcd(a,b)=\gcd(b,r) g cd( a , b ) = g cd( b , r ) 得证。
时间复杂度为 O ( log n ) \mathcal{O}(\log n) O ( log n ) ,下面是代码实现。
int gcd ( int a , int b ){
if (b == 0 ) return a;
return gcd (b, a % b);
}
cpp
埃氏筛法#
**问题:**给定 n n n ,预处理出每个 [ 1 , n ] [1,n] [ 1 , n ] 范围内的整数是否为质数的算法。
思路 :从小到大枚举,若确定 x x x 为质数,则将 x , 2 x , 3 x , ⋯ , k x x, 2x, 3x, \cdots, kx x , 2 x , 3 x , ⋯ , k x 标记为合数。若遍历到 x x x 时,其仍然未被标记为合数,则可确定 x x x 为质数。
还有以下优化:
j j j 的初值改为 j = i ⋅ i j=i\cdot i j = i ⋅ i ,因为对于 j = i ⋅ k , ( k < i ) j=i\cdot k,(k < i) j = i ⋅ k , ( k < i ) ,合数 j j j 会被 k k k 提前筛掉。
i i i 枚举的范围为 [ 2 , n ] [2,\sqrt{n}] [ 2 , n ] ,因为对于 i > n i>\sqrt{n} i > n ,i 2 > n i^2>n i 2 > n ,起不到任何贡献。
for ( int i = 2 ; i <= n; i ++ ) is_prime[i] = true ;
for ( int i = 2 ; i * i <= n; i ++ ) {
if (is_prime[i])
for ( int j = i * i; j <= n; j += i) is_prime[j] = false ;
}
cpp
经过简单优化的埃氏筛,可证明时间复杂度为 O ( n log log n ) \mathcal{O}(n\log \log n) O ( n log log n ) ,相较于 O ( n ) \mathcal{O}(n) O ( n ) 的线性筛,埃氏筛并不实用。
欧拉筛法#
欧拉筛法,又称线性筛 ,顾名思义,其时间复杂度为 O ( n ) \mathcal{O}(n) O ( n ) 。
思路 :在埃氏筛法中,每个合数可能会被筛多次,若每个合数恰好被筛一次,时间复杂度就是线性了。在线性筛中,每一个合数都是被最小的质因子筛掉 ,不过这在常见的代码实现中并不显然。
int flag[N + 10 ], pri[N + 10 ], cnt = 0 ;
inline void Euler () {
flag[ 1 ] = 1 ;
for ( int i = 2 ; i <= N; i ++ ) {
if ( ! flag[i]) pri[ ++ cnt] = i;
for ( int j = 1 ; i * pri[j] <= N && j <= cnt; j ++ ) {
flag[i * pri[j]] = 1 ;
if (i % pri[j] == 0 ) break ;
}
}
}
cpp
线性筛莫比乌斯函数#
μ ( n ) = { 1 n = 1 0 n 含有平方因子 ( − 1 ) k k 为 n 的本质不同质因子个数 \mu(n)=
\begin{cases}
1&n=1\\
0&n\text{ 含有平方因子}\\
(-1)^k&k\text{ 为 }n\text{ 的本质不同质因子个数}\ \end{cases} μ ( n ) = ⎩ ⎨ ⎧ 1 0 ( − 1 ) k n = 1 n 含有平方因子 k 为 n 的本质不同质因子个数
莫比乌斯函数是积性函数。
int flag[N + 10 ], pri[N + 10 ], mu[N + 10 ], cnt;
mu[ 1 ] = 1 ;
for ( int i = 2 ; i <= N; i ++ ) {
if ( ! flag[i]) pri[ ++ cnt] = i, mu[i] = - 1 ;
for ( int j = 1 ; j <= cnt && i * pri[j] <= N; j ++ ) {
flag[i * pri[j]] = 1 ;
if (i % pri[j] == 0 ) break ;
else mu[i * pri[j]] = - mu[i];
}
}
cpp
观察线性筛的过程,当 i m o d pri j = 0 i\bmod \operatorname{pri}_j=0 i mod pri j = 0 时,pri j ∣ i ⇒ ( pri j ) 2 ∣ i ⋅ p r i j \operatorname{pri}_j\mid i \Rightarrow (\operatorname{pri}_j)^2\mid i\cdot \operatorname{pri_j} pri j ∣ i ⇒ ( pri j ) 2 ∣ i ⋅ pr i j ,这种情况下 μ ( i ⋅ p r i j ) = 0 \mu(i\cdot \operatorname{pri_j})=0 μ ( i ⋅ pr i j ) = 0 .
否则,p r i j \operatorname{pri_j} pr i j 是第一次对 i ⋅ p r i j i\cdot \operatorname{pri_j} i ⋅ pr i j 产生贡献,μ ( i ⋅ p r i j ) = − μ ( i ) \mu(i\cdot \operatorname{pri_j})=-\mu(i) μ ( i ⋅ pr i j ) = − μ ( i ) .
欧拉函数与欧拉定理#
给定 n ∈ N ∗ n\in \Bbb{N}^{*} n ∈ N ∗ ,欧拉函数是小于等于 n n n 的所有数中与 n n n 互质的数的个数,记为 φ ( n ) \varphi(n) φ ( n ) .
形式化地,有:
φ ( n ) = ∑ i = 1 n [ gcd ( i , n ) = 1 ] \varphi(n)=\sum_{i=1}^{n} [\gcd(i,n)=1] φ ( n ) = i = 1 ∑ n [ g cd( i , n ) = 1 ]
积性函数#
积性函数 :满足 f ( m ⋅ n ) = f ( m ) ⋅ f ( n ) f(m\cdot n)=f(m)\cdot f(n) f ( m ⋅ n ) = f ( m ) ⋅ f ( n ) ,gcd ( m , n ) = 1 \gcd(m,n)=1 g cd( m , n ) = 1 .
完全积性函数 :满足 f ( m ⋅ n ) = f ( m ) ⋅ f ( n ) f(m\cdot n)=f(m)\cdot f(n) f ( m ⋅ n ) = f ( m ) ⋅ f ( n ) ,不要求 m , n m,n m , n 互质。
可以证明,欧拉函数是积性函数 。
求单个数的欧拉函数#
有结论:
φ ( x ) = x ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) ⋯ ( 1 − 1 p n ) \varphi(x)=x(1-\frac{1}{p_1})(1-\frac{1}{p_2})\cdots(1-\frac{1}{p_n}) φ ( x ) = x ( 1 − p 1 1 ) ( 1 − p 2 1 ) ⋯ ( 1 − p n 1 )
其中 p 1 , p 2 , ⋯ , p n p_1,p_2,\cdots,p_n p 1 , p 2 , ⋯ , p n 是 x x x 的所有质因数,例如:φ ( 12 ) = 12 × ( 1 − 1 2 ) × ( 1 − 1 3 ) = 4 \varphi(12)=12\times(1-\frac{1}{2})\times(1-\frac{1}{3})=4 φ ( 12 ) = 12 × ( 1 − 2 1 ) × ( 1 − 3 1 ) = 4 .
推导过程如下。
首先,显然 φ ( 1 ) = 1 \varphi(1)=1 φ ( 1 ) = 1 ,且若 p p p 是质数,有 φ ( p ) = p − 1 \varphi(p)=p-1 φ ( p ) = p − 1 .
给定质数 p p p 和正整数 k k k ,考虑计算 φ ( p k ) \varphi(p^k) φ ( p k ) 。
根据质数的性质,只有 p p p 的倍数与 p k p^k p k 不互质,因此一种计算方法是:用总数 p k p^k p k 减去 p p p 的倍数个数,即:
φ ( p k ) = p k − p k − 1 = p k × ( 1 − 1 p ) \varphi(p^k)=p^k-p^{k-1}=p^k\times (1-\frac{1}{p}) φ ( p k ) = p k − p k − 1 = p k × ( 1 − p 1 )
根据积性函数性质可得:
φ ( x ) = φ ( p 1 k 1 ) × φ ( p 2 k 2 ) × ⋯ × φ ( p n k n ) = p 1 k 1 ( 1 − 1 p 1 ) p 2 k 2 ( 1 − 1 p 2 ) ⋯ p n k n ( 1 − 1 p n ) = x ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) ⋯ ( 1 − 1 p n ) \begin{aligned}
\varphi(x)&=\varphi(p_1^{k_1})\times\varphi(p_2^{k_2})\times\cdots\times\varphi(p_n^{k_n})\\
&=p_1^{k_1}(1-\frac{1}{p_1})p_2^{k_2}(1-\frac{1}{p_2})\cdots p_n^{k_n}(1-\frac{1}{p_n})\\
&=x(1-\frac{1}{p_1})(1-\frac{1}{p_2})\cdots(1-\frac{1}{p_n})
\end{aligned} φ ( x ) = φ ( p 1 k 1 ) × φ ( p 2 k 2 ) × ⋯ × φ ( p n k n ) = p 1 k 1 ( 1 − p 1 1 ) p 2 k 2 ( 1 − p 2 1 ) ⋯ p n k n ( 1 − p n 1 ) = x ( 1 − p 1 1 ) ( 1 − p 2 1 ) ⋯ ( 1 − p n 1 )
inline int phi ( int n ) {
int res = n;
for ( int i = 2 ; i * i <= n; i ++ ) { // 分解质因数
if (n % i == 0 ) { // i 是 n 的质因子
res = res / i * (i - 1 );
while (n % i == 0 ) n /= i;
}
}
if (n > 1 ) //最后可能还剩一个最大的质因子
res = res / n * (n - 1 );
return res;
}
cpp
欧拉定理#
任意选取 a , n ∈ N ∗ a,n\in \Bbb{N}^{*} a , n ∈ N ∗ 满足 gcd ( a , n ) = 1 \gcd(a, n)=1 g cd( a , n ) = 1 ,则有:
a φ ( n ) ≡ 1 ( m o d n ) a^{\varphi(n)}\equiv1\pmod n a φ ( n ) ≡ 1 ( mod n )
费马小定理#
对任意给定的正整数 a a a 和质数 p p p ,都有:
a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv 1\pmod p a p − 1 ≡ 1 ( mod p )
容易发现,这是欧拉定理的特殊情况,证明不再赘述。
扩展欧几里得算法#
扩展欧几里得算法,用于求解一元一次不定方程 a x + b y = gcd ( a , b ) ax+by=\gcd(a,\,b) a x + b y = g cd( a , b ) ,其中 a , b a,b a , b 为已知系数,x , y x,y x , y 为未知数。
裴蜀定理#
设 a , b a,b a , b 是不全为零的整数,则存在整数 x , y x,y x , y 使得 a x + b y = gcd ( a , b ) ax+by=\gcd(a, b) a x + b y = g cd( a , b ) .
因此,扩展欧几里得算法所求解的不定方程一定有整数解。
要求出一组满足方程条件的整数解 ( x , y ) (x,y) ( x , y ) .
考虑欧几里得算法的过程,gcd ( a , b ) = gcd ( b , a m o d b ) \gcd(a, b)=\gcd(b, a\bmod b) g cd( a , b ) = g cd( b , a mod b ) .
记 d = gcd ( a , b ) d=\gcd(a, b) d = g cd( a , b ) ,则要求解的方程为 a x + b y = d ax+by=d a x + b y = d .
关注欧几里得算法的递归过程 ,( a , b ) (a,b) ( a , b ) 递归至 ( b , a m o d b ) (b, a\bmod b) ( b , a mod b ) 后,有方程 b ⋅ x 1 + ( a m o d b ) ⋅ y 1 = d b\cdot x_1+(a\bmod b)\cdot y_1=d b ⋅ x 1 + ( a mod b ) ⋅ y 1 = d .
现在要找到 ( x , y ) (x,y) ( x , y ) 与 ( x 1 , y 1 ) (x_1,y_1) ( x 1 , y 1 ) 之间的等量关系,即:从递归的深层推到浅层 。
根据 m o d \bmod mod 运算的定义:
b ⋅ x 1 + ( a m o d b ) ⋅ y 1 = d ⇒ b ⋅ x 1 + ( a − ⌊ a b ⌋ × b ) ⋅ y 1 = d b\cdot x_1+(a\bmod b)\cdot y_1=d
\Rightarrow b\cdot x_1+(a-\left\lfloor\frac{a}{b}\right\rfloor\times b)\cdot y_1=d b ⋅ x 1 + ( a mod b ) ⋅ y 1 = d ⇒ b ⋅ x 1 + ( a − ⌊ b a ⌋ × b ) ⋅ y 1 = d
整理得到:
d = b ⋅ x 1 + ( a − ⌊ a b ⌋ × b ) ⋅ y 1 = b ⋅ x 1 + a ⋅ y 1 − ( ⌊ a b ⌋ × b ) ⋅ y 1 = a ⋅ y 1 + b ⋅ ( x 1 − ⌊ a b ⌋ × y 1 ) \begin{aligned}
d&=b\cdot x_1+(a-\left\lfloor\frac{a}{b}\right\rfloor\times b)\cdot y_1\\
&=b\cdot x_1+a\cdot y_1-(\left\lfloor\frac{a}{b}\right\rfloor\times b)\cdot y_1\\
&=a\cdot y_1+b\cdot(x_1-\left\lfloor\frac{a}{b}\right\rfloor\times y_1)
\end{aligned}\\ d = b ⋅ x 1 + ( a − ⌊ b a ⌋ × b ) ⋅ y 1 = b ⋅ x 1 + a ⋅ y 1 − ( ⌊ b a ⌋ × b ) ⋅ y 1 = a ⋅ y 1 + b ⋅ ( x 1 − ⌊ b a ⌋ × y 1 )
与方程 a x + b y = d ax+by=d a x + b y = d 对应,可知一种可行的对应关系 为:
{ x = y 1 y = x 1 − ⌊ a b ⌋ × y 1 \left\{
\begin{array}{l}
x=y_1\\
y=x_1-\left\lfloor\frac{a}{b}\right\rfloor\times y_1
\end{array}
\right. { x = y 1 y = x 1 − ⌊ b a ⌋ × y 1
按照这样的等量关系,在递归过程中计算即可。
欧几里得算法的递归基为 b = 0 b=0 b = 0 ,此时 gcd ( a , b ) = a \gcd(a,b)=a g cd( a , b ) = a ,方程 a x + b y = d ax+by=d a x + b y = d 的解为 x = 0 , y = 1 x=0,y=1 x = 0 , y = 1 .
int exgcd ( int a , int b , int & x , int & y ) { // 函数返回值为gcd(a,b)
if (b == 0 ) {
x = 1 , y = 0 ;
return a;
}
int r = exgcd (b, a % b, x, y); //先递归
int t = x; //再计算x,y
x = y;
y = t - a / b * y;
return r;
}
cpp
可以证明,这种方法求出 a x + b y = d ax+by=d a x + b y = d 的特解 ( x , y ) (x,y) ( x , y ) 必满足 ∣ x ∣ ≤ b |x|\leq b ∣ x ∣ ≤ b ,∣ y ∣ ≤ a |y|\leq a ∣ y ∣ ≤ a .
假设已求出特解为 ( x 0 , y 0 ) (x_0,y_0) ( x 0 , y 0 ) ,满足 a x 0 + b y 0 = gcd ( a , b ) ax_0+by_0=\gcd(a,\,b) a x 0 + b y 0 = g cd( a , b ) .
仍然记 d = gcd ( a , b ) d=\gcd(a, b) d = g cd( a , b ) ,则有
a ( x 0 + k b d ) + b ( y 0 − k a d ) = d , k ∈ Z a(x_0+k\frac{b}{d})+b(y_0-k\frac{a}{d})=d,k\in \Bbb{Z} a ( x 0 + k d b ) + b ( y 0 − k d a ) = d , k ∈ Z
显然,k b d k\dfrac{b}{d} k d b 和 k a d k\dfrac{a}{d} k d a 是在保证解仍为整数的前提下,可取到的最小整系数 。
所以方程的通解为:
{ x = x 0 + k b d y = y 0 − k a d \left\{
\begin{array}{l}
x=x_0+k\frac{b}{d}\\y=y_0-k\frac{a}{d}
\end{array}
\right. { x = x 0 + k d b y = y 0 − k d a
对于任意二元一次不定方程 a x + b y = c , ( a , b , c ∈ Z ) ax+by=c,(a,b,c\in \Bbb{Z}) a x + b y = c , ( a , b , c ∈ Z ) ,当且仅当 d ∣ c d\mid c d ∣ c 时才有整数解。
在求出 a x + b y = gcd ( a , b ) ax+by=\gcd(a,b) a x + b y = g cd( a , b ) 后,可以得到方程特解为 ( c d x 0 , c d y 0 ) (\frac{c}{d}x_0, \frac{c}{d}y_0) ( d c x 0 , d c y 0 ) ,且有通解为:
{ x = c d x 0 + k b d y = c d y 0 − k a d \left\{
\begin{array}{l}
x=\frac{c}{d}x_0+k\frac{b}{d}\\y=\frac{c}{d}y_0-k\frac{a}{d}\end{array}
\right. { x = d c x 0 + k d b y = d c y 0 − k d a
最小非负整数解#
由通解形式,可用 (x % (b/d) + (b/d)) % (b/d)
求得最小非负整数解。
这是因为在 C++ 语言中,负整数 x x x 进行运算 x % p
的结果满足 − p < s ≤ 0 -p<s\leq 0 − p < s ≤ 0 .
同余方程#
同余方程,指形如 a x ≡ b ( m o d c ) ax\equiv b\pmod c a x ≡ b ( mod c ) 的方程。其中 a , b , c a,b,c a , b , c 是已知量,x x x 是未知量。
注意到:
a x ≡ b ( m o d c ) ⇔ x a + k c = b ax\equiv b\pmod c\Leftrightarrow xa+kc=b a x ≡ b ( mod c ) ⇔ x a + k c = b
于是转化为一般的一元一次不定方程 ,可用扩展欧几里得算法 求解。
乘法逆元#
给定两个整数 a a a 和 p p p ,假设存在 x x x 使得 a x ≡ 1 ( m o d p ) ax\equiv 1\pmod p a x ≡ 1 ( mod p ) ,则称 x x x 为 a a a 关于 p p p 的乘法逆元 。
乘法逆元又称数论倒数,是模意义下的除法,可记为 x ≡ a − 1 ( m o d p ) x\equiv a^{-1}\pmod p x ≡ a − 1 ( mod p ) .
可以证明,a a a 关于 p p p 的逆元存在的充要条件是 gcd ( a , p ) = 1 \gcd(a,p)=1 g cd( a , p ) = 1 .
求一个数的逆元#
将上面式子变形,变成a x + k p = 1 ax+kp=1 a x + k p = 1 ,用扩展欧几里得能够求出一个x x x .
若 p p p 是质数,可利用费马小定理 求逆元。
a p − 1 m o d p = ( a p − 2 × a ) m o d p = ( x × a ) m o d p = 1 a^{p-1}\bmod p=(a^{p-2}\times a)\bmod p=(x\times a)\bmod p=1 a p − 1 mod p = ( a p − 2 × a ) mod p = ( x × a ) mod p = 1
所以 a a a 关于 p p p 的逆元是 a p − 2 m o d p a^{p-2}\bmod p a p − 2 mod p 的值,用快速幂优化,时间复杂度为 O ( log n ) \mathcal{O}(\log n) O ( log n ) .
代码实现不难。
求一组数的逆元#
给定 a 1 , a 2 , ⋯ , a n a_1, a_2, \cdots, a_n a 1 , a 2 , ⋯ , a n 和模数 p p p ,若他们都与 p p p 互质,则可以在 O ( n + log a i ) \mathcal{O}(n+\log a_i) O ( n + log a i ) 的时间复杂度内计算出每个数的逆元。
预处理前缀积 s i = ( ∏ i = 1 i a i ) m o d p \operatorname{s}_i=(\prod_{i=1}^i a_i) \bmod p s i = ( ∏ i = 1 i a i ) mod p ,先计算出前缀积数组的逆元 t i t_i t i .
t n t_n t n 可通过扩展欧几里得算法或快速幂计算得到,对于 t 1 ∼ n − 1 t_{1\sim n-1} t 1 ∼ n − 1 ,可以递推计算:
在模意义下,有:
t i ≡ 1 a 1 a 2 ⋯ a i ( m o d p ) t i + 1 ≡ 1 a 1 a 2 ⋯ a i a i + 1 ( m o d p ) t_i\equiv \frac{1}{a_1a_2\cdots a_i}\pmod p\\
t_{i+1}\equiv\frac{1}{a_1a_2\cdots a_i a_{i+1}}\pmod p t i ≡ a 1 a 2 ⋯ a i 1 ( mod p ) t i + 1 ≡ a 1 a 2 ⋯ a i a i + 1 1 ( mod p )
所以:
t i ≡ 1 a 1 a 2 ⋯ a i ≡ a i + 1 a 1 a 2 ⋯ a i a i + 1 ≡ t i + 1 ⋅ a i + 1 ( m o d p ) t_i\equiv \frac{1}{a_1a_2\cdots a_i}\equiv \frac{a_{i+1}}{a_1a_2\cdots a_i a_{i+1}}\equiv t_{i+1}\cdot a_{i+1}\pmod p\\ t i ≡ a 1 a 2 ⋯ a i 1 ≡ a 1 a 2 ⋯ a i a i + 1 a i + 1 ≡ t i + 1 ⋅ a i + 1 ( mod p )
求出 t i t_i t i 后,设 a i a_i a i 的逆元为 i n v i \operatorname{inv_i} in v i ,则 inv 1 = t 1 \operatorname{inv}_1=t_1 inv 1 = t 1 ,其余项可以直接计算:
inv i ≡ a 1 a 2 ⋯ a i − 1 a 1 a 2 ⋯ a i − 1 a i ≡ t i ⋅ s i − 1 ( m o d p ) \operatorname{inv}_i\equiv \frac{a_1a_2\cdots a_{i-1}}{a_1a_2\cdots a_{i-1}a_i}\equiv t_i\cdot s_{i-1}\pmod p inv i ≡ a 1 a 2 ⋯ a i − 1 a i a 1 a 2 ⋯ a i − 1 ≡ t i ⋅ s i − 1 ( mod p )
组合数取模#
按照上面的方法,代入 a i = i a_i=i a i = i ,求出 s i = i ! s_i=i! s i = i ! ,t i = s i − 1 = 1 i ! t_i={s_i}^{-1}=\dfrac{1}{i!} t i = s i − 1 = i ! 1 .
根据组合数的定义:
C n m ≡ ( n m ) ≡ n ! m ! ( n − m ) ! ≡ s n ⋅ t m ⋅ t n − m ( m o d p ) C_n^m\equiv \binom{n}{m}\equiv \frac{n!}{m!(n-m)!}\equiv s_n\cdot t_m\cdot t_{n-m}\pmod p C n m ≡ ( m n ) ≡ m ! ( n − m )! n ! ≡ s n ⋅ t m ⋅ t n − m ( mod p )
const int N = 1 e 6 , mod = 1 e 9 + 7 ;
inline int power ( int x , int k ) {
int ret = 1 ;
while (k) {
if (k & 1 ) ret = ret * x % mod;
x = x * x % mod, k >>= 1 ;
}
return ret;
}
int fac[N + 10 ], inv[N + 10 ];
inline void init () {
fac[ 0 ] = 1 ;
for ( int i = 1 ; i <= N; i ++ ) fac[i] = fac[i - 1 ] * i % mod;
inv[N] = power (fac[N], mod - 2 );
for ( int i = N - 1 ; i >= 0 ; i -- ) inv[i] = inv[i + 1 ] * (i + 1 ) % mod;
}
cpp
中国剩余定理#
有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?
这个问题就可以转化为一个同余方程组。设 x x x 为物品数量,则有:
{ x ≡ 2 ( m o d 3 ) x ≡ 3 ( m o d 5 ) x ≡ 2 ( m o d 7 ) \left\{
\begin{align}{}
x\equiv 2\pmod 3\\
x\equiv 3\pmod 5\\
x\equiv 2\pmod 7
\end{align}{}
\right. ⎩ ⎨ ⎧ x ≡ 2 ( mod 3 ) x ≡ 3 ( mod 5 ) x ≡ 2 ( mod 7 )
中国剩余定理一元同余线性方程组:
{ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) ⋯ x ≡ a n ( m o d m n ) \left\{
\begin{aligned}&x\equiv a_1\pmod {m_1}\\&x\equiv a_2\pmod {m_2}\\&\cdots\\&x\equiv a_n\pmod {m_n}\end{aligned}
\right. ⎩ ⎨ ⎧ x ≡ a 1 ( mod m 1 ) x ≡ a 2 ( mod m 2 ) ⋯ x ≡ a n ( mod m n )
假设整数 m 1 , m 2 , ⋯ , m n m_1,m_2,\cdots,m_n m 1 , m 2 , ⋯ , m n 两两互质 ,则方程组必有解,通解可以构造如下:
设 M = ∏ m i M=\prod m_i M = ∏ m i ,并设 M i = M m i M_i=\dfrac{M}{m_i} M i = m i M , t i ≡ M i − 1 ( m o d m i ) t_i\equiv M_i^{-1}\pmod {m_i} t i ≡ M i − 1 ( mod m i ) ,有通解为:
x = a 1 t 1 M 1 + a 2 t 2 M 2 + ⋯ + a n t n M n + k M = ∑ i − 1 n a i t i M i + k M x=a_1t_1M_1+a_2t_2M_2+\cdots+a_nt_nM_n+kM=\sum_{i-1}^{n}a_it_iM_i+kM x = a 1 t 1 M 1 + a 2 t 2 M 2 + ⋯ + a n t n M n + k M = i − 1 ∑ n a i t i M i + k M
这不难理解。在 m o d m i \bmod m_i mod m i 意义下,整数 j j j 满足:
a j t j M j ≡ { 0 , j ≠ i a j , j = i ( m o d m i ) a_jt_jM_j\equiv \left\{
\begin{array}{}
0, &j\neq i\\
a_j, &j=i
\end{array}
\right. \pmod {m_i} a j t j M j ≡ { 0 , a j , j = i j = i ( mod m i )
因为 j ≠ i j\neq i j = i 时,M j ≡ 0 ( m o d m i ) M_j\equiv 0\pmod {m_i} M j ≡ 0 ( mod m i ) ,且有 t i M i ≡ 1 ( m o d m i ) t_iM_i\equiv 1\pmod {m_i} t i M i ≡ 1 ( mod m i ) .
代码实现#
程序的返回值为最小整数解。
int exgcd ( int a , int b , int & x , int & y ) {
if (b == 0 ) {
x = 1 ;
y = 0 ;
return a;
}
int r = exgcd (b, a % b, x, y);
int t = x;
x = y;
y = t - a / b * y;
return r;
}
int a[ 101 ], m[ 101 ];
inline int CRT ( int n ) {
int M = 1 ;
int ans = 0 ;
for ( int i = 1 ; i <= n; i ++ ) {
M *= m [i];
}
for ( int i = 1 ; i <= n; i ++ ) {
int x, y;
int Mi = M / m[i];
exgcd (Mi, m[i], x, y);
ans = (ans + Mi * x * a[i]) % M;
}
if (ans < 0 ) ans += M;
return ans;
}
cpp
扩展中国剩余定理#
同样用于求解同余方程组,与中国剩余定理的区别是模数不一定互质 。
考虑两个同余方程:
{ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) \left\{
\begin{aligned}x\equiv a_1 \pmod{m_1}\\
x\equiv a_2\pmod{m_2}
\end{aligned}
\right. { x ≡ a 1 ( mod m 1 ) x ≡ a 2 ( mod m 2 )
可以写为:
{ x + k 1 m 1 = a 1 ( 1 ) x + k 2 m 2 = a 2 ( 2 ) \left\{
\begin{array}{}
x+k_1m_1=a_1&&(1)\\
x+k_2m_2=a_2&&(2)
\end{array}
\right. { x + k 1 m 1 = a 1 x + k 2 m 2 = a 2 ( 1 ) ( 2 )
( 1 ) − ( 2 ) (1)-(2) ( 1 ) − ( 2 ) 得到:
m 1 k 1 − m 2 k 2 = a 1 − a 2 m_1k_1-m_2k_2=a_1-a_2 m 1 k 1 − m 2 k 2 = a 1 − a 2
可见,方程形如 a x + b y = c ax+by=c a x + b y = c ,所以有解条件是 gcd ( m 1 , m 2 ) ∣ ( a 1 − a 2 ) \gcd(m1,m2)\mid (a_1-a_2) g cd( m 1 , m 2 ) ∣ ( a 1 − a 2 ) .
用扩展欧几里得算法求得可行解 ( K 1 , K 2 ) (K_1, K_2) ( K 1 , K 2 ) ,因此 K 1 K_1 K 1 的通解为:
K 1 + m 2 d ⋅ C K_1+\frac{m_2}{d}\cdot C K 1 + d m 2 ⋅ C
代入 ( 1 ) (1) ( 1 ) ,得到:
x + K 1 m 1 + m 2 m 1 d ⋅ C = a 1 x+K_1m_1+\frac{m_2m_1}{d}\cdot C=a_1 x + K 1 m 1 + d m 2 m 1 ⋅ C = a 1
写成同余方程的形式:
x ≡ a 1 − K 1 m 1 ( m o d lcm ( m 1 , m 2 ) ) x\equiv a_1-K_1m_1 \pmod{\operatorname{lcm}(m_1, m_2)} x ≡ a 1 − K 1 m 1 ( mod lcm ( m 1 , m 2 ))
这样就把两个同余方程合并成了一个。
同理,可以将 n n n 个方程组成的同余方程组化为一个同余方程,最后用扩展欧几里得算法求解 x x x .
卢卡斯定理#
对于质数 p p p ,有:
( n m ) m o d p = ( ⌊ n p ⌋ ⌊ m p ⌋ ) ⋅ ( n m o d p m m o d p ) m o d p \binom{n}{m}\bmod p = \binom{\left\lfloor\frac{n}{p}\right\rfloor}{\left\lfloor\frac{m}{p}\right\rfloor} \cdot \binom{n\bmod p}{m\bmod p}\bmod p ( m n ) mod p = ( ⌊ p m ⌋ ⌊ p n ⌋ ) ⋅ ( m mod p n mod p ) mod p
当 p ≤ 1 0 6 p\leq 10^6 p ≤ 1 0 6 且 n , m n,m n , m 较大时实用。
扩展卢卡斯定理#
123