求解同余方程请看 http://www.cnblogs.com/candy99/p/5765986.html
[2017-02-14 19:05]
[2017-03-23 update]:看组合数学的时候发现了一个证明,记一下;改成了markdown
Chinese Remainder Theorem 中国剩余定理
定理
考虑两个方程的情况
对于n个整数(a, m+a, 2m+a , ... , (n-1)m+a), 用反证法可以证明,每个数(mod n)都有都有不同的余数
根据鸽巢原理,(b)必定作为余数出现了且只出现一次,因此这(n)个数中存在唯一的(x)满足上式
应用
求解同余方程组
其中 $ (m_i,m_j)=1, 1le i < jle n $
**直接构造解:**
(M =prod_{i=1}^{m} m_i)
首先明确(x mod M)有唯一解
$ M=m_1 * m_2 * m_3 * ... * m_k $
$ x=(sumlimits_{i=1}^k a_i* {Mover m_i} *inv({Mover m_i},m_i))mod M $
证明:
(x mod m_i)时 第(i)个式子因为有乘逆元所以结果是ai*1,其他式子的(M)可以整除(m_i)所以都是(0),方程组成立
代码
ll m[N],a[N],M=1;
void exgcd(ll a,ll b,ll &d,ll &x,ll &y){
if(b==0) d=a,x=1,y=0;
else exgcd(b,a%b,d,y,x),y-=(a/b)*x;
}
ll Inv(ll a,ll p){
ll d,x,y;
exgcd(a,p,d,x,y);
return d==1?(x+p)%p:-1;
}
ll CRT(int n,ll *a,ll *m){
ll x=0;
for(int i=1;i<=n;i++){
ll w=M/m[i];
x=(x+a[i]*w%M*Inv(w,m[i]))%M;
}
return x;
}
合并同余方程
如果不满足m两两互质,我们采用合并同余方程的方法将方程两两合并
两个方程可以写成$ x=m_1 * t_1+a_1=m_2 * t_2+a_2 $
变形 $ m_1 * t_1-m_2 * t_2=a_2-a_1 $
线性不定方程的形式
$ d=gcd(m_1,m_2) $ 有解满足 $d| (a_2-a_1) $
(exgcd)后乘(frac{a2-a1}{d})解出(t_1),当前是(mod frac{m_2}{d})意义下
代入(x = m_1*t_1+a_1)中,就变成(mod frac{m_1*m_2}{d})意义下了
所以合并后方程为 $xequiv m_1*t_1 + a_1pmod {lcm(m_1,m_2)} $
注意 (t_1)理论上是绝对值最小解,但也许是因为乘了(frac{a2-a1}{d})之后吧求最小的时候不能简单的if(t1<0) t1+=m2啦,需要模一下再变正
代码
void exgcd(ll a,ll b,ll &d,ll &x,ll &y){
if(b==0) d=a,x=1,y=0;
else exgcd(b,a%b,d,y,x),y-=(a/b)*x;
}
int main(){
freopen("in","r",stdin);
while(scanf("%lld",&n)!=EOF){
int flag=0;
m1=read();a1=read();n--;
while(n--){
m2=read();a2=read();
if(flag) continue;
ll d,t1,t2;
exgcd(m1,m2,d,t1,t2);
if((a2-a1)%d){flag=1;continue;}
t1*=(a2-a1)/d;
m2/=d;
t1=(t1%m2+m2)%m2;
a1=a1+m1*t1;
m1*=m2;
}
if(flag) puts("-1");
else printf("%lld
",a1);
}
}