找了半天发现网上居然找不到完整的证明,只能自己默默花时间推了。。。
求满足 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时便是最小整数解
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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;
}