同余问题共7part,我的博客链接:
一元线性同余方程组
形如:
{
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)\\ &\vdots \\ x\equiv a_n&(\mod m_n)\\ \end{cases}
⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧x≡a1x≡a2x≡an(modm1)(modm2)⋮(modmn)
方法一:两两相消
先看两个方程:
{
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≡a1(modm1)x≡a2(modm2)
转化为
{
x
+
m
1
k
1
=
a
1
x
+
m
2
k
2
=
a
2
(1)
\begin{cases}{} x+m_1k_1=a_1\tag{1}\\ x+m_2k_2=a_2\\ \end{cases}
{x+m1k1=a1x+m2k2=a2(1)
则得到
a
1
−
m
1
k
1
=
a
2
−
m
2
k
2
a_1-m_1k_1=a_2-m_2k_2
a1−m1k1=a2−m2k2 即
m
1
k
1
−
m
2
k
2
=
a
1
−
a
2
m_1k_1-m_2k_2=a_1-a_2
m1k1−m2k2=a1−a2 则有解的等价条件为
gcd
(
m
1
,
m
2
)
∣
(
a
1
−
a
2
)
\gcd(m_1,m_2)\mid(a_1-a_2)
gcd(m1,m2)∣(a1−a2) 用上一节的一元线性同余方程解法解出
k
1
k_1
k1 的一个特解
k
′
k'
k′ ,于是通解
k
1
=
k
′
+
c
m
2
gcd
(
m
1
,
m
2
)
,
c
∈
N
k_1=k'+\frac{cm_2}{\gcd(m_1,m_2)},c\in N
k1=k′+gcd(m1,m2)cm2,c∈N 代入
(
1
)
(1)
(1) 式可得,
x
=
a
1
−
m
1
k
′
+
c
m
1
m
2
gcd
(
m
1
,
m
2
)
x=a_1-m_1k'+\frac{cm_1m_2}{\gcd(m_1,m_2)}
x=a1−m1k′+gcd(m1,m2)cm1m2 于是转换为了新的同余方程:
x
≡
a
1
−
m
1
k
′
(
m
o
d
lcm
(
m
1
,
m
2
)
)
x\equiv a_1-m_1k'(\mod \text{lcm}(m_1,m_2))
x≡a1−m1k′(modlcm(m1,m2)) 依次合并即可解得最终答案。
// 求解 x==a[] mod m[],数组从0开始编号,共n个式子
int mod_equations(int a[], int m[], int n)
{
int a1 = a[0], a2, m1 = m[0], m2, k1, k, d;
for (int i = 1; i < n; i++)
{
a2 = a[i], m2 = m[i];
int k2, tmp;
exgcd(m1, m2, d, k1, k2);
if ((a2 - a1) % d)
return -1;
tmp = m2 / d;
k1 = (k1 * (a2 - a1) / d % tmp + tmp) % tmp; // 特解k',保证最小正数
a1 += m1 * k1;
m1 *= m2 / d;
a1 = (a1 + m1) % m1;
}
return a1; // 通解是a1+cm1,c是整数
}
方法二:中国剩余定理
条件:原方程组 m 1 , m 2 ⋯ m n m_1,m_2\cdots m_n m1,m2⋯mn 互素
定理:有模 M = m 1 ⋅ m 2 ⋯ m n M=m_1\cdot m_2\cdots m_n M=m1⋅m2⋯mn 的唯一解
推导:令 M i = M / m i M_i=M/m_i Mi=M/mi ,由互素知 gcd ( M i , m i ) = 1 \gcd(M_i,m_i)=1 gcd(Mi,mi)=1 ,又 M i ⋅ M i − 1 ≡ 1 ( m o d m i ) M_i\cdot M_i^{-1}\equiv 1(\mod m_i) Mi⋅Mi−1≡1(modmi) ,则通解为 c M + ∑ i = 1 n a i M i M i − 1 cM+\sum_{i=1}^{n}a_iM_iM_i^{-1} cM+i=1∑naiMiMi−1
// 中国剩余定理解一元线性同余方程组,数组从0标号
ll CRT(int a[], int m[], int n)
{
ll M = 1ll, ans = 0;
for (int i = 0; i < n; i++)
M *= m[i];
for (int i = 0; i < n; i++)
ans = (ans + a[i] * M % M * inv(M) % M) % M;
return ans;
}