中国剩余定理
设
m
1
,
m
2
,
⋯
,
m
n
m1, m2,\cdots, m_n
m1,m2,⋯,mn是两两互质的整数,对于下式的一元线性同余方程组有整数解
{
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(\bmod m_1) \\ x \equiv a_2(\bmod m_2) \\ \qquad \vdots \\ x \equiv a_n (\bmod m_n) \end{cases}
⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧x≡a1(modm1)x≡a2(modm2)⋮x≡an(modmn)
解为:
x
=
∑
i
=
1
n
a
i
∗
M
i
∗
M
i
−
1
x = \sum_{i=1}^{n}a_i * M_i * M_i^{-1}
x=i=1∑nai∗Mi∗Mi−1
其中:
m
=
∏
i
=
1
n
m
i
,
M
i
=
m
m
i
m = \prod_{i=1}^{n}m_i,M_i = \frac{m}{m_i}
m=∏i=1nmi,Mi=mim,
M
i
−
1
M_i^{-1}
Mi−1为
M
i
M_i
Mi的逆元,可以使用扩展欧几里得求解
具体证明可参考百度百科:中国剩余定理
代码模板:
LL CRT(LL a[], LL m[], int n)
{
LL M = 1, ans = 0;
for(int i = 1; i <= n; ++i) M *= m[i];
for(int i = 1; i <= n; ++i)
{
LL x, y;
LL Mi = M / m[i];
exgcd(Mi, m[i], x, y);
x = ((x % m[i]) + m[i] ) % m[i];
ans = (ans + a[i] * Mi * x ) ;
}
return ans;
}
扩展中国剩余定理
原题链接(模板题)
同样的,给出一个一元线性同余方程组:
{
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(\bmod m_1) \\ x \equiv a_2(\bmod m_2) \\ \qquad \vdots \\ x \equiv a_n (\bmod m_n) \end{cases}
⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧x≡a1(modm1)x≡a2(modm2)⋮x≡an(modmn)
不保证
m
1
,
m
2
,
⋯
,
m
n
m_1, m_2, \cdots, m_n
m1,m2,⋯,mn两两互质。
先考虑前两个式子拿出来:
x
=
k
1
∗
a
1
+
m
1
①
x
=
k
2
∗
a
2
+
m
2
x = k_1 * a_1 + m_1① \\ x = k_2 * a_2 + m_2
x=k1∗a1+m1①x=k2∗a2+m2
两式相减有:
k
1
∗
a
1
+
k
2
∗
(
−
a
2
)
=
m
2
−
m
1
②
k_1 * a_1 + k_2 * (-a_2) = m_2 - m_1 ②
k1∗a1+k2∗(−a2)=m2−m1②
这个式子两种情况:
- 设 d = g c d ( a 1 , − a 2 ) d = gcd(a_1, -a_2) d=gcd(a1,−a2),如果 d ∤ ( m 2 − m 1 ) d∤(m_2 - m_1) d∤(m2−m1),则无解
- 否则可以用扩展欧几里得求出一组解
k
1
′
{k_1}'
k1′和
k
2
′
{k_2}'
k2′,设
d
=
g
c
d
(
a
1
,
−
a
2
)
,
y
=
m
2
−
m
1
d
d = gcd(a_1, -a_2),y = \frac{m_2-m_1}{d}
d=gcd(a1,−a2),y=dm2−m1
我们将 k 1 ′ {k_1}' k1′和 k 2 ′ {k_2}' k2′都扩大 y y y倍后,能满足 ② ② ②式
接下来就是需要找到最小的解了。
我们有性质:
k
1
=
k
1
+
k
∗
a
2
d
③
k
2
=
k
2
+
k
∗
a
1
d
k_1 = k_1 + k * \frac{a_2}{d} ③\\ \quad\\ k_2 = k_2 + k * \frac{a_1}{d}
k1=k1+k∗da2③k2=k2+k∗da1
其中:
k
∈
Z
k \in Z
k∈Z,此时
k
1
,
k
2
k_1, k_2
k1,k2仍然满足
②
②
②式
证明如下:
我们将新的
k
1
,
k
2
k_1,k_2
k1,k2带入②式有:
(
k
1
+
k
∗
a
2
d
)
∗
a
1
+
(
k
2
+
k
∗
a
1
d
)
∗
(
−
a
2
)
=
k
1
∗
a
1
+
k
∗
a
1
∗
a
2
d
+
k
2
∗
(
−
a
2
)
+
k
∗
a
1
∗
(
−
a
2
)
d
=
k
1
∗
a
1
+
k
2
∗
(
−
a
2
)
(k_1 + k * \frac{a_2}{d})*a_1 + (k_2 + k * \frac{a_1}{d}) * (-a_2) \\ \quad \\=k_1*a_1+k*\frac{a_1*a_2}{d} +k_2*(-a_2) + k*\frac{a_1*(-a_2)}{d} \\ \quad \\ = k_1*a_1 + k_2*(-a_2)
(k1+k∗da2)∗a1+(k2+k∗da1)∗(−a2)=k1∗a1+k∗da1∗a2+k2∗(−a2)+k∗da1∗(−a2)=k1∗a1+k2∗(−a2)
证毕
我们需要一个最小的非负这整数解,我们对
③
③
③式取模
a
b
s
(
a
2
d
)
abs(\frac{a_2}{d})
abs(da2)的到:
k
1
=
k
1
m
o
d
a
b
s
(
a
2
d
)
k_1 = k_1 \bmod abs(\frac{a_2}{d})
k1=k1modabs(da2)
- 取绝对值,是为了因为不知道 a 2 d \frac{a_2}{d} da2的正负性,同时使得 k 1 k_1 k1尽可能的小
最后我们把得到的
k
1
k_1
k1带回①式有:
x
=
(
k
1
+
k
∗
a
2
d
)
∗
a
1
+
m
1
x
=
k
1
∗
a
1
+
m
1
+
k
∗
a
1
∗
a
2
d
k
i
∗
a
i
m
i
x
=
k
∗
a
1
∗
a
2
d
⏞
+
k
1
∗
a
1
+
m
1
⏞
x = (k_1 + k * \frac{a_2}{d}) * a_1 + m_1 \\ \quad \\ x = k_1*a_1 + m_1 + k*\frac{a_1*a_2}{d} \\ k_i*a_i \quad \qquad \quad m_i \\ x = \overbrace{k *\frac{a_1*a_2}{d}} + \overbrace{k_1*a_1 + m_1}
x=(k1+k∗da2)∗a1+m1x=k1∗a1+m1+k∗da1∗a2ki∗aimix=k∗da1∗a2
+k1∗a1+m1
这样:我们就把前两个式子合并为一个式子了,这样子取合并下面的式子。经过
n
−
1
n-1
n−1次合并,就将
n
n
n个式子合并为一个式子了。
扩展中国剩余定理模板:
LL exgcd(LL a, LL b, LL &x, LL &y)
{
if(!b) {
x = 1, y = 0;
return a;
}
LL x1, y1, Gcd;
Gcd = exgcd(b, a % b, x1, y1);
x = y1;
y = x1 - a / b * y1;
return Gcd;
}
LL inline Mod(LL a, LL b)
{
return ((a % b) + b) % b;
}
LL EXCRT(LL a[], LL m[], int n)
{
LL a1 = a[1], m1 = m[1];
for(int i = 2; i <= n; ++i)
{
LL a2 = a[i], m2 = m[i], k1, k2;
LL d = exgcd(a1, -a2, k1, k2);
if((m2 - m1) % d)
return -1;
LL y = (m2 - m1) / d;
k1 = Mod(k1 * (m2 - m1) / d, abs(a2 / d));
m1 = k1 * a1 + m1;
a1 = abs(a2 / d * a1);
}
return m1;
}
另外:
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 50;
LL a[N], m[N];
LL exgcd(LL a, LL b, LL &x, LL &y)
{
if(!b) {
x = 1, y = 0;
return a;
}
LL x1, y1, Gcd;
Gcd = exgcd(b, a % b, x1, y1);
x = y1;
y = x1 - a / b * y1;
return Gcd;
}
LL inline Mod(LL a, LL b)
{
return ((a % b) + b) % b;
}
LL EXCRT(LL a[], LL m[], int n)
{
LL a1 = a[1], m1 = m[1];
for(int i = 2; i <= n; ++i)
{
LL a2 = a[i], m2 = m[i], k1, k2;
LL d = exgcd(a1, -a2, k1, k2);
if((m2 - m1) % d)
return -1;
LL y = (m2 - m1) / d;
k1 = Mod(k1 * (m2 - m1) / d, abs(a2 / d));
m1 = k1 * a1 + m1;
a1 = abs(a2 / d * a1);
}
return m1;
}
int main()
{
int n;
cin >> n;
for(int i = 1; i <= n; ++i) cin >> a[i] >> m[i];
cout << EXCRT(a, m, n) << endl;
}