中国剩余定理:
找了半天发现网上居然找不到完整的证明,只能自己默默花时间推了。。。
求满足
x
≡
2
(
m
o
d
  
3
)
  
,
  
x
≡
3
(
m
o
d
  
5
)
  
,
  
x
≡
2
(
m
o
d
  
7
)
x\equiv 2(mod\;3)\;,\;x\equiv 3(mod\;5)\;,\;x\equiv 2(mod\;7)
x≡2(mod3),x≡3(mod5),x≡2(mod7)的最小正整数解x
可相加性证明:
   \\\;
140满足%3=2,而且是5和7的倍数,也就是说加上140对其他数的取余结果不会有影响
   \\\;
同理,其他的数加上这个数就可以在不影响自生取余的情况下附带满足%3=2的性质
   \\\;
所以把所有这些数加起来就是满足答案的一个解,而在这个时候对于这个得到的数尽可能的消小(减去所有数的公倍数不会改变性质),就是答案
数学表示为:
  
\\\;
设
问
题
为
x
%
m
=
a
,
第
i
个
式
子
的
m
为
m
i
,
a
为
a
i
,
M
为
所
有
m
的
乘
积
,
M
i
为
M
/
m
i
问题为x\%m=a, 第i个式子的m为m_i , a为a_i , M为所有m的乘积 , M_i为M/m_i
问题为x%m=a,第i个式子的m为mi,a为ai,M为所有m的乘积,Mi为M/mi
列出:
k
1
∗
7
∗
5
≡
2
(
m
o
d
  
3
)
k_1*7*5\equiv 2(mod \;3)
k1∗7∗5≡2(mod3)
k
2
∗
7
∗
3
≡
3
(
m
o
d
  
5
)
k_2*7*3\equiv 3(mod \;5)
k2∗7∗3≡3(mod5)
k
3
∗
3
∗
5
≡
2
(
m
o
d
  
7
)
k_3*3*5\equiv 2(mod \;7)
k3∗3∗5≡2(mod7)
左边的部分为上面的140,63,30
k怎么求呢?以第一个为例:
先
k
1
∗
7
∗
5
≡
2
∗
(
7
∗
5
)
∗
i
n
v
(
7
∗
5
,
3
)
k_1*7*5\equiv2*(7*5)*inv(7*5,3)
k1∗7∗5≡2∗(7∗5)∗inv(7∗5,3),使得右边为
7
7
7和
5
5
5的倍数,但是我们这里不进行对
3
3
3的取模操作
这样既保证了右边部分可以 % m i = a i \% m_i = a_i %mi=ai,而且是其他m的倍数
因为这里的m都是互质的所以上文中提到的公倍数就是M
代码:
void exgcd(LL a,LL b,LL &x,LL &y) { //a在模b下的逆元为x
if(b==0) {
x=1;
y=0;
return;
}
exgcd(b,a%b,x,y);
LL tp=x;
x=y;
y=tp-a/b*y;
}
int main() {
while(cin>>n) {
M=1;
ans=0;
for(LL i=1; i<=n; i++) {
m[i]=read(),a[i]=read(); // ans % m = a;
M*=m[i];
}
for(LL i=1; i<=n; i++) {
LL Mi=M/m[i];
LL inv,y;
exgcd(Mi,m[i],x,y);
ans+=a[i]*Mi%M *inv%M;
ans%=M;
}
cout<<ans<<'\n';
}
}
扩展中国剩余定理(不互质情况下的做法):
上面这么做的前提是多个m互质,如果不互质,就不能这么做了
我们就需要用合并的方法,合并下面的两个方程:
x
=
a
1
+
m
1
x
1
x
=
a
2
+
m
2
x
2
x=a_1+m_1x_1\\x=a_2+m_2x_2
x=a1+m1x1x=a2+m2x2
x
=
a
1
+
m
1
x
1
=
a
2
+
m
2
x
2
x=a_1+m_1x_1=a_2+m_2x_2
x=a1+m1x1=a2+m2x2
m
1
x
1
+
m
2
(
−
x
2
)
=
a
2
−
a
1
m_1x_1+m_2(-x_2)=a_2-a_1
m1x1+m2(−x2)=a2−a1
用扩展欧几里得求这个二元一次方程组,设
d
=
g
c
d
(
m
1
,
m
2
)
,
c
=
a
2
−
a
1
d=gcd(m_1,m_2),c=a_2-a_1
d=gcd(m1,m2),c=a2−a1
求出x1的最小整数解
x
1
=
(
x
1
∗
c
d
%
m
2
d
+
m
2
d
)
%
m
2
d
x_1=(x_1*\dfrac{c}{d}\%\dfrac{m2}{d}+\dfrac{m2}{d})\%\dfrac{m2}{d}
x1=(x1∗dc%dm2+dm2)%dm2,有
x
=
m
1
(
x
1
+
k
∗
(
m
2
/
d
)
)
+
a
1
x
=
(
m
1
∗
m
2
/
d
)
∗
k
+
(
m
1
∗
x
1
+
a
1
)
x=m_1(x_1+k*(m2/d))+a1\\x=(m_1*m_2/d)*k+(m_1*x_1+a_1)
x=m1(x1+k∗(m2/d))+a1x=(m1∗m2/d)∗k+(m1∗x1+a1)
为什么是k*(m2/d)?
   \\\;
因为扩展欧几里得的x有多个解,所以可以加上这部分,在保证等式成立的前提下,使等式的右边出现一个变量使得保持结构的不变性,以迭代
便化成了一个新的式子,不断迭代就可以出结果
当然,k=0时便是最小整数解
模板(poj 2891)
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<cstdlib>
using namespace std;
#define LL long long
const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 5;
int n;
void ex_gcd(LL a, LL b, LL &d, LL &x, LL &y) {
if (!b) {
d = a, x = 1, y = 0;
} else {
ex_gcd(b, a % b, d, y, x);
y -= x * (a / b);
}
}
LL ex_crt(LL *m, LL *r, int n) {
LL M = m[1], R = r[1], x, y, d;
for (int i = 2; i <= n; ++i) {
ex_gcd(M, m[i], d, x, y);
if ((r[i] - R) % d)
return -1;
x = (r[i] - R) / d * x % (m[i] / d);
R += x * M;
M = M / d * m[i];
R %= M;
}
return R > 0 ? R : R + M;
}
LL m[maxn], r[maxn];
int main() {
while (~scanf("%d",&n)) {
for (int i = 1; i <= n; ++i)
scanf("%lld%lld", &m[i], & r[i]);
printf("%lld\n",ex_crt(m,r,n));
}
return 0;
}