在第二十九章中,作者详细地讲解了中国剩余定理的使用,在开始本章节的讲解之前,建议读者先去看上一章节的讲解。传送门:中国剩余定理与线性同余方程组
中国剩余定理是用来解决线性同余方程组的,但是有一个很苛刻的要求就是:方程组中的同余方程的模数必须是互质的。只有这样我们才能套用中国剩余定理。
扩展中国剩余定理就是为了解决方程组中模数不互质的情况。
{ x ≡ a 1 m o d ( m 1 ) x ≡ a 2 m o d ( m 2 ) x ≡ a 3 m o d ( m 3 ) . . . 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_3\ mod(m_3)\\ ...\\ x\equiv a_n\ mod(m_n) \end{cases} ⎩ ⎨ ⎧x≡a1 mod(m1)x≡a2 mod(m2)x≡a3 mod(m3)...x≡an mod(mn)
我们取出前两个式子:
x ≡ a 1 m o d ( m 1 ) x ≡ a 2 m o d ( m 2 ) x\equiv a_1\ mod(m_1)\\ x\equiv a_2\ mod(m_2)\\ x≡a1 mod(m1)x≡a2 mod(m2)
这两个式子能够写成:
x = k 1 m 1 + a 1 x = k 2 m 2 + a 2 x=k_1m_1+a_1\\ x=k_2m_2+a_2 x=k1m1+a1x=k2m2+a2
两个式子相减:
k 1 m 1 + a 1 = k 2 m 2 + a 2 k_1m_1+a_1=k_2m_2+a_2 k1m1+a1=k2m2+a2
将上面的式子移项:
k 1 m 1 − k 2 m 2 = a 2 − a 1 k_1m_1-k_2m_2=a_2-a_1 k1m1−k2m2=a2−a1
我们把 k 1 k_1 k1和 − k 2 -k_2 −k2看作 x k x_k xk和 y k y_k yk。
那么这个式子就可以写成:
x k m 1 + y k m 2 = ( a 2 − a 1 ) x_km_1+y_km_2=(a_2-a_1) xkm1+ykm2=(a2−a1)
根据我们的裴蜀定理:
如果 g c d ( m 1 , m 2 ) ∣ ( a 2 − a 1 ) gcd(m_1,m_2)|(a_2-a_1) gcd(m1,m2)∣(a2−a1)
我们就能够根据扩展欧几里德算法计算出系数。
如果上述整除的关系不成立,说明这个式子是无解的。
我们现在来讨论整除关系成立的情况:
我们可以根据欧几里得算法计算出:
x 0 m 1 + y 0 m 2 = g c d ( m 1 , m 2 ) x_0m_1+y_0m_2=gcd(m1,m2) x0m1+y0m2=gcd(m1,m2)的特殊解。
为了得到我们的 x k x_k xk和 y k y_k yk,我们需要给两遍同乘 ( a 2 − a 1 ) g c d ( m 1 , m 2 ) \frac{(a_2-a_1)}{gcd(m_1,m_2)} gcd(m1,m2)(a2−a1)
所以我们的 x x x和 y y y的特殊解可以写成:
x k 1 = x 0 ∗ ( a 2 − a 1 ) g c d ( m 1 , m 2 ) y k 1 = y 0 ∗ ( a 2 − a 1 ) g c d ( m 1 , m 2 ) x_{k1}=x_0*\frac{(a_2-a_1)}{gcd(m_1,m_2)}\\ y_{k1}=y_0*\frac{(a_2-a_1)}{gcd(m_1,m_2)} xk1=x0∗gcd(m1,m2)(a2−a1)yk1=y0∗gcd(m1,m2)(a2−a1)
但是这是一组特殊解:
当我们式子为:
x 0 m 1 + y 0 m 2 = n ∗ g c d ( m 1 , m 2 ) x_0m_1+y_0m_2=n*gcd(m1,m2) x0m1+y0m2=n∗gcd(m1,m2)
此时我们可以通过特殊解构造出一般解:
我们构造的原则是下面的等式恒成立:
x 0 m 1 + y 0 m 2 = g c d ( m 1 , m 2 ) x_0m_1+y_0m_2=gcd(m1,m2) x0m1+y0m2=gcd(m1,m2)
因此我们给我们的特殊解进行如下变形:
x k = x k 1 + m 2 g c d ( m 1 , m 2 ) ∗ n y k = y k 1 − m 1 g c d ( m 1 , m 2 ) ∗ n x_k=x_{k1}+\frac{m_2}{gcd(m_1,m_2)}*n\\ y_k=y_{k1}-\frac{m_1}{gcd(m_1,m_2)}*n xk=xk1+gcd(m1,m2)m2∗nyk=yk1−gcd(m1,m2)m1∗n
我们将这两个式子带回: x 0 m 1 + y 0 m 2 = g c d ( m 1 , m 2 ) x_0m_1+y_0m_2=gcd(m1,m2) x0m1+y0m2=gcd(m1,m2)
发现依然是成立的。
所以我们构造的 x k x_{k} xk和 y k y_{k} yk就是通解。
此时带回我们的
x = k 1 m 1 + a 1 x=k_1m_1+a_1 x=k1m1+a1
我们前面是将 k 1 k_1 k1看作了 x k x_k xk,所以我们的式子可以变形为:
x = m 2 ∗ m 1 g c d ( m 1 , m 2 ) ∗ n + x k 1 ∗ m 1 + a 1 x=\frac{m_2*m_1}{gcd(m_1,m_2)}*n+x_{k1}*m_1+a_1 x=gcd(m1,m2)m2∗m1∗n+xk1∗m1+a1
而 m 2 ∗ m 1 g c d ( m 1 , m 2 ) \frac{m_2*m_1}{gcd(m_1,m_2)} gcd(m1,m2)m2∗m1就是我们的最小公倍数。因为:
最大公因数 ∗ 最小公倍数 = 两个数的乘积 最大公因数*最小公倍数=两个数的乘积 最大公因数∗最小公倍数=两个数的乘积
所以我们让:
l c m ( m 1 , m 2 ) = m 2 ∗ m 1 g c d ( m 1 , m 2 ) lcm(m_1,m_2)=\frac{m_2*m_1}{gcd(m_1,m_2)} lcm(m1,m2)=gcd(m1,m2)m2∗m1
所以我们的式子可以化简为:
x = l c m ( m 1 , m 2 ) ∗ n + x k 1 m 1 + a 1 x=lcm(m_1,m_2)*n+x_{k1}m_1+a_1 x=lcm(m1,m2)∗n+xk1m1+a1
而这个式子可以写成:
x ≡ r ( m o d m ) x\equiv r(mod\ m) x≡r(mod m)
其中:
{ r = k 1 m 1 + a 1 m = l c m ( m 1 , m 2 ) \begin{cases} r={k_1}m_1+a_1\\ m=lcm(m_1,m_2) \end{cases} {r=k1m1+a1m=lcm(m1,m2)
经过上述推导,我们将两个同余方程:
x = k 1 m 1 + a 1 x = k 2 m 2 + a 2 x=k_1m_1+a_1\\ x=k_2m_2+a_2 x=k1m1+a1x=k2m2+a2
合并成了一个同余方程:
x ≡ r ( m o d m ) x\equiv r(mod\ m) x≡r(mod m)
因此,我们只需要不断地去合并,经过 n − 1 n-1 n−1次合并,我们就能够把 n n n个同余方程合并成一个同余方程。
当只有一个合并方程的时候,我们肯定就能够利用扩展欧几里得算法进行计算了。
#include
#include
using namespace std;
typedef long long LL;
LL exgcd(LL a, LL b, LL &x, LL &y)
{
if (!b)
{
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main()
{
int n;
cin >> n;
LL x = 0, m1, a1;
cin >> m1 >> a1;
for (int i = 0; i < n - 1; i ++ )
{
LL m2, a2;
cin >> m2 >> a2;
LL k1, k2;
LL d = exgcd(m1, m2, k1, k2);
if ((a2 - a1) % d)
{
x = -1;
break;
}
k1 *= (a2 - a1) / d;
k1 = (k1 % (m2/d) + m2/d) % (m2/d);
x = k1 * m1 + a1;
LL m = abs(m1 / d * m2);
a1 = k1 * m1 + a1;
m1 = m;
}
if (x != -1) x = (a1 % m1 + m1) % m1;
cout << x << endl;
return 0;
}