中国剩余定理——杨子曰数学
超链接:数学合集
问:今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?
换成人话(这才不是人话好吗):
解方程:
{
x
≡
a
1
(
m
o
d
m
1
)
x
≡
a
2
(
m
o
d
m
2
)
x
≡
a
3
(
m
o
d
m
3
)
⋮
x
≡
a
n
(
m
o
d
m
n
)
\left\{ \begin{aligned} x & \equiv a_1(mod\ m_1)\\ x & \equiv a_2(mod\ m_2)\\ x & \equiv a_3(mod\ m_3)\\ \vdots\\ x & \equiv a_n(mod\ m_n)\\ \end{aligned} \right.
⎩⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎧xxx⋮x≡a1(mod m1)≡a2(mod m2)≡a3(mod m3)≡an(mod mn)
(
m
1
,
m
2
,
m
3
⋯
⋯
m
n
互
质
,
I
t
′
v
e
r
y
i
m
p
o
r
t
a
n
t
)
(m_1,m_2,m_3 \cdots \cdots m_n互质,It'\ very \ important)
(m1,m2,m3⋯⋯mn互质,It′ very important)
中国剩余定理
我们先来讲肿么做,再来讲为啥可以这样做
How to do?
- 求出
m
i
m_i
mi的lcm
比如文章最上面的那道题,我们先求出 m = l c m ( 3 , 5 , 7 ) = 105 m=lcm(3,5,7)=105 m=lcm(3,5,7)=105 - 让
M
i
=
m
m
i
M_i=\frac{m}{m_i}
Mi=mim
于是,我们有了: M 1 = 105 / 3 = 35 , M 2 = 105 / 5 = 21 , M 3 = 105 / 7 = 15 M_1=105/3=35,M_2=105/5=21,M_3=105/7=15 M1=105/3=35,M2=105/5=21,M3=105/7=15 - 让M_i扩大一定的倍数s,使得
s
∗
M
i
m
o
d
m
i
=
a
i
s*M_i\ mod\ m_i=a_i
s∗Mi mod mi=ai
在这个例子中: M 1 m o d m 1 = 35 m o d 3 = 2 M_1\ mod\ m_1=35\ mod\ 3=2 M1 mod m1=35 mod 3=2
欧,发现不用扩大就已经满足了 M 1 m o d m 1 = a 1 M_1\ mod\ m_1=a_1 M1 mod m1=a1
继续, M 2 m o d m 2 = 21 m o d 5 = 1 M_2\ mod\ m_2=21\ mod\ 5=1 M2 mod m2=21 mod 5=1
余数需要变成3,那我们就把被除数变成原来的3倍
也就是 63 m o d 5 = 3 63\ mod\ 5=3 63 mod 5=3
然后, M 3 m o d m 3 = 15 m o d 7 = 1 M_3\ mod\ m_3=15\ mod\ 7=1 M3 mod m3=15 mod 7=1
余数得是2,那我们就要把 M 3 M_3 M3变为2倍
也就是: 30 m o d 7 = 2 30\ mod\ 7=2 30 mod 7=2 - 把扩大后的
m
i
m_i
mi全部加起来,我们就得到了答案:
a n s = M 1 + M 2 + M 3 = 35 + 63 + 30 = 128 ans=M_1+M_2+M_3=35+63+30=128 ans=M1+M2+M3=35+63+30=128 - 如果你喜欢的话把
a
n
s
m
o
d
m
(
它
们
的
l
c
m
)
ans\ mod\ m(它们的lcm)
ans mod m(它们的lcm),使答案落在0~m之间
a n s = a n s m o d m = 128 m o d 105 = 23 ans=ans\ mod\ m=128\ mod\ 105=23 ans=ans mod m=128 mod 105=23
OK,完事
Why这样做我们可以找到答案?
其实
M
i
M_i
Mi就是除了
m
i
m_i
mi以外的其他所有
m
i
m_i
mi的lcm(←别忘了它们是两两互质的)
也就是说M_i扩大了任意被模其他
m
i
m_i
mi都是不受影响的
那我们就可以随便扩大,让
M
i
m
o
d
m
i
=
a
i
M_i\ mod \ m_i=a_i
Mi mod mi=ai不就好了吗?
至于如何让
M
i
M_i
Mi变成我们想要的值,其实我们先要算的是
M
i
M_i
Mi在模
m
i
m_i
mi下的逆元(逆元,什么?戳),也就是我们想让
k
∗
M
i
m
o
d
m
i
=
1
k*M_i\ mod\ m_i=1
k∗Mi mod mi=1,这是再把
k
∗
M
i
k*M_i
k∗Mi扩大相应的倍数,就行了
OK,完事
int china()
{
int ans=0,lcm=1,x,y;
for(int i=1;i<=n;i++) lcm*=m[i];
for(int i=1;i<=n;i++)
{
int M=lcm/m[i];
ex_gcd(M,m[i],x,y);
x=(x%m[i]+m[i])%m[i];
ans=(ans+M*x*a[i])%lcm;
}
return (ans+lcm)%lcm;
}
扩展中国剩余定理
{
x
≡
a
1
(
m
o
d
m
1
)
x
≡
a
2
(
m
o
d
m
2
)
x
≡
a
3
(
m
o
d
m
3
)
⋮
x
≡
a
n
(
m
o
d
m
n
)
\left\{ \begin{aligned} x & \equiv a_1(mod\ m_1)\\ x & \equiv a_2(mod\ m_2)\\ x & \equiv a_3(mod\ m_3)\\ \vdots\\ x & \equiv a_n(mod\ m_n)\\ \end{aligned} \right.
⎩⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎧xxx⋮x≡a1(mod m1)≡a2(mod m2)≡a3(mod m3)≡an(mod mn)
一样的方程只不过这里的
m
i
m_i
mi不互质了
它与中国剩余定理半毛钱关系也木有~~
我们先来看一看如果只有两个方程的情况:
{
x
≡
a
1
(
m
o
d
m
1
)
x
≡
a
2
(
m
o
d
m
2
)
\left\{ \begin{aligned} x & \equiv a_1(mod\ m_1)\\ x & \equiv a_2(mod\ m_2)\\ \end{aligned} \right.
{xx≡a1(mod m1)≡a2(mod m2)
这个方程组其实可以转化成:
{
x
=
a
1
+
k
1
∗
m
1
x
=
a
2
+
k
2
∗
m
2
\left\{ \begin{aligned} x=a_1+k_1*m_1\\ x=a_2+k_2*m_2\\ \end{aligned} \right.
{x=a1+k1∗m1x=a2+k2∗m2
然后我们把两式相减,就得到了:
k
1
∗
m
1
−
k
2
∗
m
2
=
a
2
−
a
1
k_1*m_1-k_2*m_2=a_2-a_1
k1∗m1−k2∗m2=a2−a1
哦,有没有发现这是一个形如这个的方程:
a
∗
x
+
b
∗
y
=
c
a*x+b*y=c
a∗x+b∗y=c
也就是说我们可以用扩展欧几里得解决(那是什么?戳)
于是,我们就得到了 k 1 k_1 k1和 − k 2 -k_2 −k2
然后 x 0 = k 1 ∗ m 1 + a 1 x_0=k_1*m_1+a_1 x0=k1∗m1+a1
我们也就知道了方程的通解是: x = x 0 + m ∗ l c m ( m 1 , m 2 ) x=x_0+m*lcm(m_1,m_2) x=x0+m∗lcm(m1,m2)(不停扩大或减小 l c m ( m 1 , m 2 ) lcm(m_1,m_2) lcm(m1,m2)对它们的余数是没有影响的)
也就是说: x ≡ x 0 ( m o d l c m ( m 1 , m 2 ) ) x \equiv x_0(mod\ lcm(m_1,m_2)) x≡x0(mod lcm(m1,m2))
有没有发现我们把两个方程合并成了 一个!!
然后不停地合并下去,我们就得到了答案!
poj2891:
#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
int n;
ll gcd(ll a,ll b){
return b==0?a:gcd(b,a%b);
}
void ex_gcd(ll a,ll b,ll &x,ll &y){
if(!b) x=1,y=0;
else ex_gcd(b,a%b,y,x),y-=(a/b)*x;
}
ll mul(ll a,ll b,ll p){
ll ans=0;
while(b){
if (b%2) ans=(ans+a)%p;
a=(a+a)%p;
b/=2;
}
return ans;
}
int main(){
ll m2,a2,m,ans;
while(~scanf("%d",&n)){
bool flag=0;
scanf("%lld%lld",&m,&ans);
for (int i=2;i<=n;i++){
scanf("%lld%lld",&m2,&a2);
a2=(a2-ans%m2+m2)%m2;//a2-a1
ll d=gcd(m2,m),x,y;
if (a2%d) flag=1;//方程无解
ex_gcd(m,m2,x,y);
x=mul(x,a2/d,m2);//解k1*m1-k2*m2=a2-a1 (如果你想过掉【洛谷P4777】这里要加一个龟速乘)
ans+=x*m;
m*=m2/d;//m1=lcm(m1,m2)
ans=(ans%m+m)%m;
}
if (flag) printf("-1\n");
else printf("%lld\n",ans);
}
return 0;
}
OK,完事
参考:
https://www.cnblogs.com/freinds/p/6388992.html
https://blog.youkuaiyun.com/niiick/article/details/80229217
https://www.cnblogs.com/Miracevin/p/9254795.html
https://www.luogu.org/recordnew/show/19877167
https://www.luogu.org/recordnew/show/19921331
于HG机房