题目描述
求满足以下同余方程的最小非负整数解x(
m
i
m_i
mi并不互质)
{
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{matrix} x\ \equiv\ a_1\ (mod\ m_1)) \\ x\ \equiv\ a_2\ (mod\ m_2)) \\ ......\\ x\ \equiv\ a_n\ (mod\ m_n)) \\ \end{matrix}\right.
⎩⎪⎪⎨⎪⎪⎧x ≡ a1 (mod m1))x ≡ a2 (mod m2))......x ≡ an (mod mn))
思路
它与中国剩余定理的区别在于模数并不互质
这就导致了使用逆元时,模数并不是一个质数,所以无法用逆元
我们设前k-1个方程的解为x
m
u
l
=
∏
i
=
1
k
−
1
m
i
mul = \prod_{i = 1} ^ {k - 1}m_i
mul=∏i=1k−1mi
但为了防止溢出
m
u
l
=
L
C
M
i
=
1
k
−
1
m
i
mul=LCM_{i = 1} ^{k - 1}m_i
mul=LCMi=1k−1mi
通解是
x
=
x
+
i
∗
m
u
l
(
i
∈
Z
)
x = x + i * mul(i \in Z)
x=x+i∗mul(i∈Z)
则加入第k个方程后
我们要找一个
t
t
t使得
x
+
t
∗
m
u
l
≡
a
k
(
m
o
d
m
k
)
x + t * mul \equiv a_k(mod\ m_k)
x+t∗mul≡ak(mod mk)
转换一下
t
∗
m
u
l
≡
a
k
−
x
(
m
o
d
m
k
)
t * mul \equiv a_k - x(mod\ m_k)
t∗mul≡ak−x(mod mk)
m
k
∗
p
+
t
∗
m
u
l
=
a
k
−
x
m_k * p + t * mul = a_k - x
mk∗p+t∗mul=ak−x
然后用扩欧求出
m
k
∗
p
+
t
∗
m
u
l
=
1
m_k * p + t * mul = 1
mk∗p+t∗mul=1
再两边同乘上
a
k
−
x
a_k - x
ak−x
取最小正整数解,同时更新
m
u
l
mul
mul和
x
x
x
ps:
因为有些地方乘的时候可能会溢出
所以要用long double或龟速乘
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define ll long long
using namespace std;
ll A[100250], M[100250], X[100250];
ll n, mul, Mul;
void exgcd(ll a, ll b, ll &x, ll &y)
{
if(b == 0){x = 1, y = 0; return;}
exgcd(b, a % b, x, y);
ll z = x; x = y; y = z - y * (a / b);
}
ll gcd(ll a, ll b)
{return (b) ? gcd(b, a % b) : a;}
int main()
{
scanf("%lld", &n);
mul = Mul = 1;
for(int i = 1; i <= n; ++i)
scanf("%lld%lld", &M[i], &A[i]),
Mul = (M[i] / gcd(M[i], Mul)) * Mul;
mul = M[1], X[1] = A[1];
for(ll i = 2; i <= n; ++i)
{
ll x = 0, y = 0;
ll gcd_ = gcd(mul, M[i]);
ll p = A[i] - X[i - 1];
ll q = p / gcd(mul, M[i]);
exgcd(mul, M[i], x, y);
ll gcdd = M[i] / gcd_;
while(x <= 0)x += gcdd;
ll aa = (long double)x * q / gcdd;
X[i] = (long double)x * q - (long double)aa * gcdd;
X[i] = X[i - 1] + X[i] * mul;
mul = mul * (M[i] / gcd(mul, M[i]));
}
printf("%lld", (X[n] % mul + mul) % mul);
return 0;
}