中国剩余定理
m 1 , m 2 , m 3 ⋯ m n m_1,m_2,m_3\cdots m_n m1,m2,m3⋯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\ ({\rm mod}\ m_1) \\ x\equiv a_2\ ({\rm mod}\ m_2) \\ \cdots \\ x \equiv a_n\ ({\rm mod}\ m_n)\end{cases}
⎩⎪⎪⎪⎨⎪⎪⎪⎧x≡a1 (mod m1)x≡a2 (mod m2)⋯x≡an (mod mn)
求最小的满足方程的
x
x
x。
由于网上对中国剩余定理的基本概念讲述的差不多了,这里就直接给简单证明。
为了方便后面的描述,我们定义 M = ∏ i = 1 n m i , w i = M m i M=\prod\limits_{i=1}^nm_i\ ,\ w_i=\frac{M}{m_i} M=i=1∏nmi , wi=miM(这个定义不用于后面的扩展中国剩余定理和其它情况)。
我们的目的是想对于每一个方程找出一个比较特殊的 p i p_i pi,使得 p i ≡ a i m o d m i p_i\equiv a_i\ {\rm mod}\ m_i pi≡ai mod mi,且 p i ≡ 0 m o d m j ( j ≠ i ) p_i\equiv 0\ {\rm mod}\ m_j(j≠i) pi≡0 mod mj(j=i),因为求出所有的 p i p_i pi后, ∑ i = 1 n p i m o d M \sum\limits_{i=1}^np_i\ {\rm mod}\ M i=1∑npi mod M就是我们的答案 x x x。
观察 p i p_i pi的限制,可以发现 p i p_i pi应该为除了 m i m_i mi以外的模数的最小公倍数(因为所有 m i m_i mi互质,所以也就是 w i w_i wi)的倍数,所以我们只需找到一个 p i p_i pi使得 p i = k i w i p_i=k_iw_i pi=kiwi( k i k_i ki为任意整数),且 p i ≡ a i m o d m i p_i\equiv a_i\ {\rm mod}\ m_i pi≡ai mod mi,那么这个 p i p_i pi就是我们需要的。但是 p i ≡ a i m o d m i p_i\equiv a_i\ {\rm mod}\ m_i pi≡ai mod mi这个式子并不特殊,我们需要找一些更方便求解的式子来替换它。思考一下可以发现,其实我们只需要找到在满足 p i = k i w i p_i=k_iw_i pi=kiwi的条件下,使得 p i ≡ 1 m o d m i p_i \equiv 1\ {\rm mod}\ m_i pi≡1 mod mi的数,把 p i p_i pi乘上 a i a_i ai,就得到我们想要的 p i p_i pi。
现在的问题就是如何找到满足条件的 k i k_i ki,观察式子 k i w i ≡ 1 m o d m i k_iw_i\equiv 1\ {\rm mod}\ m_i kiwi≡1 mod mi,可以看到 k i k_i ki实际上是 w i w_i wi在模 m i m_i mi意义下的逆元,所以: p i = a i × w i × k i = a i × w i × w i − 1 p_i=a_i×w_i×k_i=a_i×w_i×w_i^{-1} pi=ai×wi×ki=ai×wi×wi−1
到这里,我们就成功得出了中国剩余定理的公式:
x
≡
∑
i
=
1
k
a
i
×
w
i
×
w
i
−
1
(
m
o
d
M
)
x\equiv \sum_{i=1}^ka_i×w_i×w_i^{-1}({\rm mod}\ M)
x≡i=1∑kai×wi×wi−1(mod M)
扩展中国剩余定理
由于一般情况下模数并不是两两互素,那么之前的做法就行不通了,所以我们就需要用到扩展中国剩余定理。
我们将上面的同余方程组更改一下:
{
x
=
p
1
m
1
+
a
1
x
=
p
2
m
2
+
a
2
⋯
x
=
p
n
m
n
+
a
n
\begin{cases}x=p_1m_1+a_1 \\ x=p_2m_2+a_2 \\ \cdots \\x=p_nm_n+a_n\end{cases}
⎩⎪⎪⎪⎨⎪⎪⎪⎧x=p1m1+a1x=p2m2+a2⋯x=pnmn+an
对于这种形式,比较好的办法就是两两合并成一个方程。以合并前两个方程为例:
根据等式可得: m 1 p 1 + a 1 = m 2 p 2 + a 2 m_1p_1+a_1=m_2p_2+a_2 m1p1+a1=m2p2+a2
移项: m 1 p 1 − m 2 p 2 = a 2 − a 1 m_1p_1-m_2p_2=a_2-a_1 m1p1−m2p2=a2−a1
由于 p 2 p_2 p2可正可负,为了方便推导,我们变一下号: m 1 p 1 + m 2 p 2 = a 2 − a 1 m_1p_1+m_2p_2=a_2-a_1 m1p1+m2p2=a2−a1
根据贝祖定理可以知道,若 g c d ( m 1 , m 2 ) ∤ a 2 − a 1 gcd(m_1,m_2)\nmid a_2-a_1 gcd(m1,m2)∤a2−a1,那么整个同余方程无解。
在有解的情况下则可用扩展欧几里得求出一组 x , y x,y x,y使得: m 1 x + m 2 y = g c d ( m 1 , m 2 ) m_1x+m_2y=gcd(m_1,m_2) m1x+m2y=gcd(m1,m2)
两边同时乘上 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: m 1 x ( a 2 − a 1 ) g c d ( m 1 , m 2 ) + m 2 y ( a 2 − a 1 ) g c d ( m 1 , m 2 ) = a 2 − a 1 m_1\frac{x(a_2-a_1)}{gcd(m_1,m_2)}+m_2\frac{y(a_2-a_1)}{gcd(m_1,m_2)}=a_2-a_1 m1gcd(m1,m2)x(a2−a1)+m2gcd(m1,m2)y(a2−a1)=a2−a1
根据多项式恒等定理: p 1 = x ( a 2 − a 1 ) g c d ( m 1 , m 2 ) , p 2 = y ( a 2 − a 1 ) g c d ( m 1 , m 2 ) p_1=\frac{x(a_2-a_1)}{gcd(m_1,m_2)},p_2=\frac{y(a_2-a_1)}{gcd(m_1,m_2)} p1=gcd(m1,m2)x(a2−a1),p2=gcd(m1,m2)y(a2−a1)
那么:
m
1
p
1
+
a
1
=
a
2
−
m
2
p
2
=
x
m_1p_1+a_1=a_2-m_2p_2=x
m1p1+a1=a2−m2p2=x
所以我们目前得到了满足前两个方程的一个
x
x
x的解,
合并后的同余方程为:
x
≡
m
1
p
1
+
a
1
m
o
d
l
c
m
(
m
1
,
m
2
)
x \equiv m_1p_1+a_1\ {\rm mod}\ {\rm lcm}(m_1,m_2)
x≡m1p1+a1 mod lcm(m1,m2)
或者也可以是:
x ≡ a 2 − m 2 p 2 m o d l c m ( m 1 , m 2 ) x \equiv a_2-m_2p_2\ {\rm mod}\ {\rm lcm}(m_1,m_2) x≡a2−m2p2 mod lcm(m1,m2)
若我们要得到满足所有方程的解,那么我们只要把合并后的方程再与其它的方程合并,最后剩下的一个方程就是答案。
应用:【模板】扩展中国剩余定理
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
const int N=100010;
int n;
LL x,y,lcm,equ[N][2];
LL multi(LL a,LL b,LL p)
{
a=(a%p+p)%p;b=(b%p+p)%p;LL ans=0;
for(;a;a>>=1,b=(b*2)%p)if(a&1)ans=(ans+b)%p;
return ans;
}
LL exgcd(LL a,LL b,LL &x,LL &y)
{
if(!b)
{
x=1,y=0;
return a;
}
LL val=exgcd(b,a%b,x,y);
LL t=x;x=y;y=t-a/b*y;return val;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld%lld",&equ[i][1],&equ[i][0]);
for(int i=1;i<n;i++)
{
LL val=exgcd(equ[i][1],equ[i+1][1],x,y);
lcm=equ[i][1]/val*equ[i+1][1];
equ[i+1][1]=lcm;
val=multi(x,(equ[i+1][0]-equ[i][0])/val,lcm);
equ[i+1][0]=(multi(equ[i][1],val,lcm)+equ[i][0])%lcm;
}
printf("%lld\n",(equ[n][0]%equ[n][1]+equ[n][1])%equ[n][1]);
return 0;
}
注意乘法溢出
其它情况
{ w 1 x ≡ a 1 ( m o d m 1 ) w 2 x ≡ a 2 ( m o d m 2 ) ⋯ w k x ≡ a k ( m o d m k ) \begin{cases} w_1x \equiv a_1\ ({mod}\ m_1) \\ w_2x\equiv a_2\ ({mod}\ m_2) \\ \cdots \\ w_kx \equiv a_k\ ({mod}\ m_k)\end{cases} ⎩⎪⎪⎪⎨⎪⎪⎪⎧w1x≡a1 (mod m1)w2x≡a2 (mod m2)⋯wkx≡ak (mod mk)
若未知数 x x x的系数不为 1 1 1,当 w i w_i wi与 m i m_i mi互质时可以用 e x g c d exgcd exgcd求出逆元将 w i w_i wi除到另一边,但是如果不互质又该怎么办?我们尝试能不能将其转换成互质的形式。
若要使得 w i w_i wi与 m i m_i mi互质,我们则需要对方程两边同时除上一个 g c d ( w i , m i ) {\rm gcd}(w_i,m_i) gcd(wi,mi),但是这样的正确性和更改后的方程又如何?
令 Q i = g c d ( w i , m i ) Q_i={\rm gcd}(w_i,m_i) Qi=gcd(wi,mi),那么 w i = Q i l i , m i = Q i r i w_i=Q_il_i,m_i=Q_ir_i wi=Qili,mi=Qiri。将方程转化为以下形式:
Q i l i × x = a i + Q i r i × q i Q_il_i×x=a_i+Q_ir_i×q_i Qili×x=ai+Qiri×qi
其中 q i q_i qi为任意整数,那么 a i = Q i ( l i × x + r i × q i ) a_i=Q_i(l_i×x+r_i×q_i) ai=Qi(li×x+ri×qi),也就是说 a i a_i ai必为 Q i Q_i Qi的倍数,若不满足的话则方程组无解,否则我们可以将方程两边同时除以 Q i Q_i Qi得到以下形式:
l i × x = a i ′ + r i × q i l_i×x=a_i'+r_i×q_i li×x=ai′+ri×qi
可以发现 l i x ≡ a i ′ m o d r i ⇔ w i x ≡ a i m o d m i l_ix\equiv a_i'\ {\rm mod}\ r_i\Leftrightarrow w_ix\equiv a_i\ {\rm mod}\ m_i lix≡ai′ mod ri⇔wix≡ai mod mi
那么我们可以将原方程替换为 l i x ≡ a i ′ m o d r i l_ix\equiv a_i'\ {\rm mod}\ r_i lix≡ai′ mod ri,因为 l i , r i l_i,r_i li,ri互质,所以我们就可以用逆元将 l i l_i li除到方程右边,然后就可以用 e x C R T exCRT exCRT求解了。