中国剩余定理
m1,m2,m3⋯mn
两两互素,有同余方程组:
⎧⎩⎨⎪⎪⎪⎪⎪⎪x≡a1 (mod m1)x≡a2 (mod m2)⋯x≡an (mod mn)
求最小的满足方程的
x
。
由于网上对中国剩余定理的基本概念讲述的差不多了,这里就直接给简单证明。
为了方便后面的描述,我们定义
M=∏i=1nmi , wi=Mmi
(这个定义不用于后面的扩展中国剩余定理和其它情况)。
我们的目的是想对于每一个方程找出一个比较特殊的
pi
,使得
pi≡ai mod mi
,且
pi≡0 mod mj(j≠i)
,因为求出所有的
pi
后,
∑i=1npi mod M
就是我们的答案
x
。
观察
pi
的限制,可以发现
pi
应该为除了
mi
以外的模数的最小公倍数(因为所有
mi
互质,所以也就是
wi
)的倍数,所以我们只需找到一个
pi
使得
pi=kiwi
(
ki
为任意整数),且
pi≡ai mod mi
,那么这个
pi
就是我们需要的。但是
pi≡ai mod mi
这个式子并不特殊,我们需要找一些更方便求解的式子来替换它。思考一下可以发现,其实我们只需要找到在满足
pi=kiwi
的条件下,使得
pi≡1 mod mi
的数,把
pi
乘上
ai
,就得到我们想要的
pi
。
现在的问题就是如何找到满足条件的
ki
,观察式子
kiwi≡1 mod mi
,可以看到
ki
实际上是
wi
在模
mi
意义下的逆元,所以:
pi=ai×wi×ki=ai×wi×w−1i
到这里,我们就成功得出了中国剩余定理的公式:
x≡∑i=1kai×wi×w−1i(mod M)
扩展中国剩余定理
由于一般情况下模数并不是两两互素,那么之前的做法就行不通了,所以我们就需要用到扩展中国剩余定理。
我们将上面的同余方程组更改一下:
⎧⎩⎨⎪⎪⎪⎪x=p1m1+a1x=p2m2+a2⋯x=pnmn+an
对于这种形式,比较好的办法就是两两合并成一个方程。以合并前两个方程为例:
根据等式可得:
m1p1+a1=m2p2+a2
移项:
m1p1−m2p2=a2−a1
由于
p2
可正可负,为了方便推导,我们变一下号:
m1p1+m2p2=a2−a1
根据贝祖定理可以知道,若
gcd(m1,m2)∤a2−a1
,那么整个同余方程无解。
在有解的情况下则可用扩展欧几里得求出一组
x,y
使得:
m1x+m2y=gcd(m1,m2)
两边同时乘上
a2−a1gcd(m1,m2)
:
m1x(a2−a1)gcd(m1,m2)+m2y(a2−a1)gcd(m1,m2)=a2−a1
根据多项式恒等定理:
p1=x(a2−a1)gcd(m1,m2),p2=y(a2−a1)gcd(m1,m2)
那么:
m1p1+a1=a2−m2p2=x
所以我们目前得到了满足前两个方程的一个
x
的解,
合并后的同余方程为:
x≡m1p1+a1 mod lcm(m1,m2)
或者也可以是:
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;
}
注意乘法溢出
其它情况
⎧⎩⎨⎪⎪⎪⎪⎪⎪w1x≡a1 (mod m1)w2x≡a2 (mod m2)⋯wkx≡ak (mod mk)
若未知数
x
的系数不为
1
,当
wi
与
mi
互质时可以用
exgcd
求出逆元将
wi
除到另一边,但是如果不互质又该怎么办?我们尝试能不能将其转换成互质的形式。
若要使得
wi
与
mi
互质,我们则需要对方程两边同时除上一个
gcd(wi,mi)
,但是这样的正确性和更改后的方程又如何?
令
Qi=gcd(wi,mi)
,那么
wi=Qili,mi=Qiri
。将方程转化为以下形式:
Qili×x=ai+Qiri×qi
其中
qi
为任意整数,那么
ai=Qi(li×x+ri×qi)
,也就是说
ai
必为
Qi
的倍数,若不满足的话则方程组无解,否则我们可以将方程两边同时除以
Qi
得到以下形式:
li×x=a′i+ri×qi
可以发现
lix≡a′i mod ri⇔wix≡ai mod mi
那么我们可以将原方程替换为
lix≡a′i mod ri
,因为
li,ri
互质,所以我们就可以用逆元将
li
除到方程右边,然后就可以用
exCRT
求解了。
应用:[NOI2018]屠龙勇士