中国剩余定理
中国剩余定理
前置芝士
在《孙子算经》中有这样一个问题:“今有物不知其数,三三数之剩二(除以3余2),五五数之剩三(除以5余3),七七数之剩二(除以7余2),问物几何?”这个问题称为“孙子问题”,该问题的一般解法国际上称为“中国剩余定理”。
定义
中国剩余定理是一种用来求解🐖如
形式的同余方程组的定理,其中,\(m_1,m_2,...,m_k\) 为两两互质的整数,我们的目的,是找出\(x\)的最小非负整数解。
推导过程
设 \(M=\prod\limits_{i=1}^km_i,M_i=\frac{M}{m_i},M_it_i\equiv 1~(mod~m_i)\) ,其中 \(1\le i\le k,t_i\) 为 \(M_i\) 在模 \(m_i\) 意义下的逆元(不会的下面会介绍扩展欧几里得的方法)。
所以我们可以构造出来一个解 \(x=\sum\limits_{i=1}^ka_iM_it_i\)
由此,任意解 \(x_0\) 即为 \(x+k*M\) ,最小整数解 \(x_{min}=x_0\%M\)
证明:
易证,在第 \(i\) 个同余方程中,对于 \(\forall~j\in [i,k]\) ,当 \(i\not=j\) 时,有
因为 \(M_k\) 里面是有 \(m_i\) 的,而当 \(i=j\) 时,有
因此,\(\sum\limits_{i=1}^ka_iM_it_i\equiv a_i~(mod~m_i)\) ,满足题意
证毕
逆元( \(ex\) 欧几里得)
扩展欧几里得用于在已知\(a,b\)的情况下,求解出一组解 \((x,y)\),使之满足 \(ax+by=gcd(a,b)=d\) 。此处,我们使用扩展欧几里得求逆元。
标准思路:
下式代入上式,并设
又因为
所以
故原方程中
此外,在开篇的方程组中,我们要求的是\(M_i\)在模\(m_i\)意义下的逆元,而之前我们设\(M_i=\frac M{m_i}\),因此\(M_i\)与\(m_i\)互质,\(gcd(M_i,m_i)=1\)。在这里我们把\(M_i\)当做\(a\),把\(m_i\)当做\(b\),即\(ax+by=1\),移项得\(ax=1-by\),显而易见\(ax\equiv 1~(mod~b)\),因此 \(x\) 即我们要求的逆元。
模板题
题目
给定\(n\)组非负整数 \(a_i,b_i\) ,求解关于 \(x\) 的方程组的最小非负整数解。
其中 \(a_i,a_j\) 互质(去掉这句话就是扩展版)
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#define ll long long
ll n,a[16],b[16],Mi[16],M=1,ans;
inline int read(){
int io=0;char in=getchar();
while(in<'0'||in>'9')in=getchar();
while(in>='0'&&in<='9')io=(io<<3)+(io<<1)+(in^'0'),in=getchar();
return io;
}
void exgcd(ll a,ll b,ll &x,ll &y){
if(b==0) {x=1;y=0;return ;}
exgcd(b,a%b,x,y);
int z=x;x=y,y=z-y*(a/b);
}
int main(){
n=read();
for(int i=1;i<=n;i++)
{
a[i]=read();
b[i]=read();
M*=a[i];
}
for(int i=1;i<=n;i++)
{
Mi[i]=M/a[i];
ll x=0,y=0;
exgcd(Mi[i],a[i],x,y);//逆元
ans+=b[i]*Mi[i]*(x<0?x+a[i]:x);
}
printf("%lld\n",ans%M);
return 0;
}