扩展中国剩余定理

扩展中国剩余定理可以用于解同余方程组:

{xr1(moda1)xr2(moda2)xrn(modan)\begin{cases}x\equiv r_1\pmod{a_1}\\x\equiv r_2\pmod{a_2}\\\cdots\\x\equiv r_n\pmod{a_n}\\\end{cases}

其中aia_i不一定两两互质

它是怎么做的?

我们暂时先只考虑两个方程组:

{xr1(moda1)xr2(moda2)\begin{cases}x\equiv r_1\pmod{a_1}\\x\equiv r_2\pmod{a_2}\end{cases}

因为

x=k1a1+r1=k2a2+r2x=k_1a_1+r_1=k_2a_2+r_2

所以有

k1a1k2a2=r1r2k_1a_1-k_2a_2=r_1-r_2

这是一个不定方程,我们可以通过exgcd来求解

根据裴蜀定理,设g=gcd(a1,a2)g=gcd(a_1,a_2),该方程有解当且仅当(r1r2)g(r_1-r_2)|g

如果有解,那么我们可以通过求出k1k_1或者k2k_2来求得xx,设其为x0x_0

那么这个方程组的通解为

x=x0+k×lcm(a1,a2)x=x_0+k\times lcm(a_1,a_2)

这样求出的xx既满足第一个方程,又满足第二个方程

而上式又可以转化成

xx0(modlcm(a1,a2))x\equiv x_0\pmod{lcm(a_1,a_2)}

所以原方程组等价于上面这一个方程


对于一般的情况,我们可以对所有同余方程逐个合并为一个同余方程,这样我们就可以获得原同余方程组的通解

例题

「poj2891」Strange Way to Express Integers

这是一个板题,直接上代码了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;

template <typename T> void read(T &t)
{
t=0;int f=0;char c=getchar();
while(!isdigit(c)){f|=c=='-';c=getchar();}
while(isdigit(c)){t=t*10+c-'0';c=getchar();}
if(f)t=-t;
}

const int maxn=100000+5;
int n;
ll a[maxn],r[maxn];

ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(!b)
{
x=1,y=0;
return a;
}
ll xx,yy,re=exgcd(b,a%b,xx,yy);
x=yy;
y=xx-a/b*yy;
return re;
}

//取模运算不需要保证在非负整数下进行,因为C++的负数取模运算仍然满足以下式子
//余数 = 被除数 - 商 * 除数
ll excrt()
{
ll M=a[1],R=r[1];
for(register int i=2;i<=n;++i)
{
ll k1,k2,g=exgcd(M,a[i],k1,k2);
if((r[i]-R)%g)return -1;
k1=(r[i]-R)/g*k1%a[i];
R+=k1*M;
M=M/g*a[i];
R%=M;
}
return (R+M)%M;
}

int main()
{
while(scanf("%d",&n)!=EOF)
{
for(register int i=1;i<=n;++i)
read(a[i]),read(r[i]);
printf("%lld\n",excrt());
}
return 0;
}