欢迎访问个人博客
简介
对于一元线性同余方程组
{
x
≡
a
1
(
m
o
d
m
1
)
x
≡
a
2
(
m
o
d
m
2
)
⋮
x
≡
a
n
(
m
o
d
m
n
)
\left\{\begin{array}{c} x \equiv a_{1}\left(\bmod m_{1}\right) \\ x \equiv a_{2}\left(\bmod m_{2}\right) \\ \vdots \\ x \equiv a_{n}\left(\bmod m_{n}\right) \end{array}\right.
⎩⎪⎪⎪⎨⎪⎪⎪⎧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 两两互质。
扩展中国剩余定理适用于
m
1
,
m
2
,
⋯
,
m
n
m_1,m_2,\cdots,m_n
m1,m2,⋯,mn 不是两两互质的情况。
求解
考虑前方程组前两个方程,
{
x
≡
a
1
(
m
o
d
m
1
)
x
≡
a
2
(
m
o
d
m
2
)
\left\{\begin{array}{c} x \equiv a_{1}\left(\bmod m_{1}\right) \\ x \equiv a_{2}\left(\bmod m_{2}\right)\end{array}\right.
{x≡a1(modm1)x≡a2(modm2),一定存在整数
p
,
q
p,q
p,q,使得
x
=
m
1
p
+
a
1
=
m
2
q
+
a
2
x=m_1p+a_1=m_2q+a_2
x=m1p+a1=m2q+a2,即
m
1
p
+
a
1
=
m
2
q
+
a
2
m_1p+a_1=m_2q+a_2
m1p+a1=m2q+a2,移项后得到
m
1
p
−
m
2
q
=
a
2
−
a
1
m_1p-m_2q=a_2-a_1
m1p−m2q=a2−a1。结合方程组前二式,得到一个裴蜀等式,由裴蜀定理,当且仅当
gcd
(
m
1
,
m
2
)
∣
(
a
2
−
a
1
)
\gcd(m_1,m_2)\mid(a_2-a_1)
gcd(m1,m2)∣(a2−a1) 时方程有解。
假设得到的裴蜀等式有解,
p
0
p_0
p0 为方程
m
1
x
+
m
2
y
=
gcd
(
m
1
,
m
2
)
m_1x+m_2y=\gcd(m_1,m_2)
m1x+m2y=gcd(m1,m2) 的特解,那么
p
p
p 的通解为
p
=
p=
p=
a
2
−
a
1
gcd
(
m
1
,
m
2
)
p
0
\frac{a_2-a_1}{\gcd(m_1,m_2)}p_0
gcd(m1,m2)a2−a1p0
+
k
m
2
gcd
(
m
1
,
m
2
)
(
k
∈
Z
)
+k\frac{m_2}{\gcd(m_1,m_2)}(k\in Z)
+kgcd(m1,m2)m2(k∈Z)。所以
x
=
m
1
p
+
a
1
=
a
2
−
a
1
gcd
(
m
1
,
m
2
)
m
1
p
0
+
a
1
+
k
lcm
(
m
1
,
m
2
)
x=m_1p+a_1=\frac{a_2-a_1}{\gcd(m_1,m_2)}m_1p_0+a_1+k\ \text{lcm}(m_1,m_2)
x=m1p+a1=gcd(m1,m2)a2−a1m1p0+a1+k lcm(m1,m2)。合并后的通解为
x
≡
a
2
−
a
1
gcd
(
m
1
,
m
2
)
m
1
p
0
+
a
1
(
m
o
d
lcm
(
m
1
,
m
2
)
)
x\equiv \frac{a_2-a_1}{\gcd(m_1,m_2)}m_1p_0+a_1\pmod{\text{lcm}(m_1,m_2)}
x≡gcd(m1,m2)a2−a1m1p0+a1(modlcm(m1,m2))。
综上所述,使用
n
n
n 次扩展欧几里得算法,不断合并同余式,最后的余项即为答案。
模板
#include<iostream>
#include<cstdio>
#define ll long long
#define N 100003
using namespace std;
int n;
ll a[N],m[N];
//---------------------------------------
//扩展欧几里得算法
//用于将两个线性同余方程合并
inline ll ex_gcd(ll a,ll b,ll &x,ll &y)
{
if(!b)
{
x=1;
y=0;
return a;
}
ll g=ex_gcd(b,a%b,x,y);
ll temp=x;
x=y;
y=temp-a/b*y;
return g;
}
//---------------------------------------
//---------------------------------------
//扩展中国剩余定理
//不断合并两个线性同余方程
//最后的A即为最小正整数解
void ex_CRT()
{
ll M=m[1],A=a[1];
int i;
for(i=2;i<=n;i++)
{
ll p,q;
ll g=ex_gcd(M,m[i],p,q);
if((a[i]-A)%g) //无解
{
puts("-1");
return;
}
p=(a[i]-A)/g*p;
//将p取为正解
p=(p%(m[i]/g)+m[i]/g)%(m[i]/g);
//合并后,方程未知数x与A模M同余
A=M*p+A;//m1p+a1
M=M/g*m[i];//lcm(m1,m2)
//对A取模
A=(A%M+M)%M;
}
cout<<A<<endl;
}
//----------------------------------------
int main()
{
cin>>n;
int i;
for(i=1;i<=n;i++) scanf("%lld%lld",m+i,a+i);
ex_CRT();
return 0;
}
洛谷P4777 【模板】扩展中国剩余定理(EXCRT) ↬ \looparrowright ↬
题目描述
给定
n
n
n 组非负整数
a
i
,
b
i
a_i, b_i
ai,bi,求解关于
x
x
x 的方程组的最小非负整数解。
{
x
≡
b
1
(
m
o
d
a
1
)
x
≡
b
2
(
m
o
d
a
2
)
⋮
x
≡
b
n
(
m
o
d
a
n
)
\left\{\begin{array}{c} x \equiv b_{1}\left(\bmod a_{1}\right) \\ x \equiv b_{2}\left(\bmod a_{2}\right) \\ \vdots \\ x \equiv b_{n}\left(\bmod a_{n}\right) \end{array}\right.
⎩⎪⎪⎪⎨⎪⎪⎪⎧x≡b1(moda1)x≡b2(moda2)⋮x≡bn(modan)
输入格式
输入第一行包含整数
n
n
n。
接下来
n
n
n 行,每行两个非负整数
a
i
,
b
i
a_i, b_i
ai,bi。
输出格式
输出一行,为满足条件的最小非负整数 x x x。
输入输出样例
输入 #1
3
11 6
25 9
33 17
输出 #1
809
说明/提示
对于
100
%
100 \%
100% 的数据,
1
≤
n
≤
10
5
1 \le n \le {10}^5
1≤n≤105,
1
≤
a
i
≤
10
12
1 \le a_i \le {10}^{12}
1≤ai≤1012,
0
≤
b
i
<
a
i
0 \le b_i < a_i
0≤bi<ai,保证所有
a
i
a_i
ai 的最小公倍数不超过
10
18
{10}^{18}
1018。
请注意程序运行过程中进行乘法运算时结果可能有溢出的风险。
数据保证有解。
分析
扩展欧几里得算法模板题,乘法运算时有可能爆 long long \text{long long} long long,需要使用 __ int128 \text{int128} int128。__ int128 \text{int128} int128 最大可达 2 128 ≈ 3.4 × 1 0 39 2^{128}\approx 3.4\times 10^{39} 2128≈3.4×1039。
代码
#include<cstdio>
#include<iostream>
#define ll long long
#define BigInt __int128
#define N 100002
using namespace std;
int n;
ll a[N],b[N];
inline BigInt ex_gcd(BigInt a,BigInt b,BigInt &x,BigInt &y)
{
if(!b)
{
x=1;
y=0;
return a;
}
BigInt g=ex_gcd(b,a%b,x,y);
BigInt temp=x;
x=y;
y=temp-a/b*y;
return g;
}
void ex_CRT()
{
BigInt A=a[1],B=b[1];
int i;
for(i=2;i<=n;i++)
{
BigInt p,q;
BigInt g=ex_gcd(A,a[i],p,q);
if((b[i]-B)%g)
{
puts("-1");
return;
}
p=(b[i]-B)/g*p;
p=(p%(a[i]/g)+a[i]/g)%(a[i]/g);
B=A*p+B;
A=A/g*a[i];
B=(B%A+A)%A;
}
cout<<(ll)B<<endl;//转为long long输出
}
int main()
{
cin>>n;
int i;
for(i=1;i<=n;i++) scanf("%lld%lld",a+i,b+i);
ex_CRT();
return 0;
}