今天做题的时候化简式子,化简到一半发现几乎就是中国剩余定理的板题,然后发现蒟蒻我不会写中国剩余定理,然后就来补习了23333333
解线性模方程
解法
( ax equiv b mod q ) 可以转化为( ax + kq = b ) 。
当( gcd(a,q)|b ) 时,方程有解,否则无解。
当( a ) 和( q ) 不互质时,令方程两边同时处以( gcd(a,q) ) ,得到:
( dfrac{a}{gcd(a,q)}x+kdfrac{q}{gcd(a,q)}=dfrac{b}{gcd(a,q)} )
也就是:
( dfrac{ax}{gcd(a,q)} equiv dfrac{b}{gcd(a,q)} mod dfrac{q}{gcd(a,q)} )
令( a_1=dfrac{a}{gcd(a,q)},b_1=dfrac{b}{gcd(a,q)},q_1=dfrac{q}{gcd(a,q)} ) ,于是有:
( a_1x equiv b_1 mod q_1 ) ,其中( a_1 ) 和( q_1 ) 互质。此时,在方程两边乘( a_1 ) 在模( q_1 ) 意义下的逆元,就可以得到:
( xequiv b_1cdot a_1^{-1} mod q_1 )
所以( x=b_1cdot a_1^{-1} + kcdot q_1 ) ,其中( kin Z^+ )
等价类
对于上式( x=b_1cdot a_1^{-1} + kcdot q_1 ) ,对于任意( i,jin Z^+,i geqslant j ) ,显然,我们有:
( (b_1cdot a_1^{-1}+icdot q_1)-(b_1cdot a_1^{-1} + jcdot q_1)= (i-j)q_1 )
如果( (b_1cdot a_1^{-1}+icdot q_1) ) 和( (b_1cdot a_1^{-1} + jcdot q_1) ) 同余,那么( (i-j)q_1 ) 是( q ) 的倍数。因此,( (i-j) ) 必须是( gcd(a,q) ) 的倍数。
换句话说,在模( q ) 的剩余系下,( ax equiv b mod q ) 恰好有( gcd(a,q) ) 个解。令( t = b_1cdot a_1^{-1} ) ,则这( gcd(a,q) ) 个解分别是:( t, t+p_1 , t + 2p_1, cdots , t + (gcd(a,q)-1)p_1 ) 。
这里稍作解释:为什么说,在模( q ) 的剩余系下,只有这( gcd(a,q) ) 个解呢?
考虑( x_1=t ) 和( x_2=t+gcd(a,q)p_1 ) ,由上面的定义可知:( gcd(a,q)p_1=p ) 。所以,( ax_1 mod p ) 和( ax_2 mod p ) 在任何意义下都是完全一致的。同理可得其他( x=t + kp_1 ) 的情况。所以,只保留这( gcd(a,q) ) 个解就能涵盖所有的情况。
解同余方程组(中国剩余定理)
变量还是一个,但是变成了如下的方程组:
egin{cases} x equiv a_1 mod m_1 cr xequiv a_2 mod m_2 cr cdots cr xequiv a_n mod m_n end{cases}
假设此时所有的( m_i ) 两两互质。令( M=prodlimits_{i=1}^{n}m_i,w_i=dfrac{M}{m_i} ) (( w_i ) 就是除了( m_i ) 以外其他( m ) 的乘积),那么有( gcd(w_i, m_i)=1 ) (( w_i ) 和( m_i ) 互质)。
所以,我们很容易求出( w_i ) 在模( m_i ) 意义下的逆元:
( t_iw_i + ym_i = 1 ) ,拓展欧几里得算法可以求出( t_i ) 。
所以,显然有( t_iw_i equiv 1 mod m_i ) (( t_i ) 是( w_i ) 的逆元)。
也就是:
( t_iw_i = km_i + 1, kin Z ) ,等价于
( t_iw_i + km_i = 1, k in Z )
两边同时乘( a_i ) ,得:
( t_ia_iw_i + ka_im_i = a_i )
我们知道,( k ) 表示的是模( m_i ) 的次数,所以我们可以把( ka_i ) 看作( k ) 。
于是我们得到:
( t_ia_iw_i + km_i = a_i )
又因为我们有:( x equiv a_i mod m_i ) (看题干),也就是:
( x = a_i + km_i )
( x + km_i = a_i )
所以,我们得到如下方程组:
( egin{cases} t_ia_iw_i + km_i = a_i cr x + km_i = a_i end{cases} )
注意这里两个( k ) 不一定相等,但是因为我们要保证的是( x ) 对( m_i ) 取模的结果是( a_i ) ,所以整数( k ) 对结果并没有影响(因为它们的乘数是( m_i ) )。所以我们得到:( x=t_ia_iw_i ) 。
由定义可知,对于任意( j eq i ) ,恒有( m_j | w_i ) 。
所以,由取模的性质,对于任意的整数( j ) 有:
( egin{align} & sumlimits_{i=1}^{n}t_ia_iw_i mod m_j cr = &left( t_ja_jw_j + sumlimits_{i=1}^{j-1}t_ia_iw_i + sumlimits_{i=j+1}^{n}t_ia_iw_i ight) mod m_j cr = & t_ja_jw_j mod m_j + sumlimits_{i=1}^{j-1}t_ia_iw_i mod w_j+ sumlimits_{i=j+1}^{n}t_ia_iw_i mod w_j cr = &t_ja_jw_j mod m_j cr = & a_jend{align} )
(因为( m_j | w_i ) ,所以( sumlimits_{i=1}^{j-1}t_ia_iw_i mod w_j = 0 ) ,( sumlimits_{i=j+1}^{n}t_ia_iw_i mod m_j = 0 ) )
所以,( x ) 的一组解就是( sumlimits_{i=1}^{n}t_ia_iw_i ) ,其中( t_i ) 是( w_i ) 关于模( m_i ) 的逆元。
在模( M ) 的剩余系下,方程组就有唯一一组解:( sumlimits_{i=1}^{n}t_ia_iw_i mod M ) 。
代码如下:
// 测试代码
#include <cstdio>
typedef long long ll;
ll exgcd(ll a, ll b, ll &x, ll &y) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
ll r = exgcd(b, a % b, x, y);
ll t = x;
x = y;
y = t - a / b * y;
return r;
}
// n个方程:x = a[i] % m[i], (0 <= i < n)
ll CRT(int n, int *a, int *m) {
ll M = 1, t, y, x = 0;
for (int i = 0; i < n; i++) M *= m[i];
for (int i = 0; i < n; i++) {
ll w = M / m[i];
exgcd(w, m[i], t, y);
x = ((x + t * w * a[i]) % M + M) % M;
}
return x;
}
int a[1000], m[1000];
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d", a + i);
for (int i = 0; i < n; i++)
scanf("%d", m + i);
printf("%lld
", CRT(n, a, m));
return 0;
}
参考资料:
中国剩余定理证明
刘汝佳《算法竞赛入门经典——训练指南》
本文迁移自作者原博客:icysky's Blog
本文作者:icysky
原文链接:同余方程和中国剩余定理
版权声明:本博客所有文章除特别声明外,均采用CC-BY-NC-SA 4.0许可协议。icysky's Blog 版权所有,转载请注明出处。