同余方程组——中国剩余定理の板子

中国剩余定理

中国剩余定理(CRT)是求解形如

xa1(mod m1)
xa2(mod m2)
xa3(mod m3)
......
xak(mod mk)

的同余方程组的一种算法。一般的中国剩余定理要求 m1,m2,...,mk 两两互质,但它的扩展算法可以处理不互质的情况。

一般情况

m1,m2,...,mk 两两互质的时候,上面提到的同余方程组一定有整数解。设 M=m1×m2×...×mk ,也就是M是所有模数的最小公倍数,那么上面的同余方程组在模M的意义下有唯一解。

那么中国剩余定理又是如何求解这个同余方程组的呢?首先我们要知道一个东西就是:如果一个整数 N 满足 N mod m=k ,那么对于任意整数v,有 (v×N) mod m(v×k) mod m 。这个东西正确性比较显然啦,设 N=r×m+k 然后往里一代就可以证明了。

那么仍然回到上面的同余方程组,如果我们能够求解出这样的一些 N1,N2,...,Nk ,满足对于每一个 Ni ,它对除了 mi 以外的所有 m 取模都是0,但除以 mi 余数为1。构造出这样的一堆东西有什么用呢?结合上面说的那个东西我们可以发现,令 N=a1N1+a2N2+...+akNk ,则N就是同余方程的一个合法整数解。因为每个 Ni 累加进去不会破坏其它模数的余数而只会影响它自己的 mi 的余数,所以这样是正确的。

于是问题就变成了如何求解 Ni 。仍然用 M 表示所有模数的最小公倍数,将 Mmi 表示为 Mi ,那么 Ni=Mi×x 。又因为 Ni mod mi=1 ,所以 Ni=y×mi+1 。把这两种表示 Ni 的方法一联立就有了 Mi×xy×mi=1

这里就体现了我们保证模数互质的重要性。因为模数们两两互质,所以 mi Mi 也互质,那么就可以把上面含有 x,y 的不定方程写作 Mix+miy=gcd(Mi,mi)

这个式子就是经典的扩展欧几里得能够解决的式子了。因为我们要求的是 x ,那么相当于求 Mi 在模 mi 意义下的逆元。这个方程一定是有解的,那么我们可以对每一个 Mi mi 都求一下对应的 x ,同时维护一个sum,每次累加上 x×Mi×ai ,最后就能求出一个解。

而因为我们可以注意到 M 是所有模数的最小公倍数,它模所有 mi 都是0,所以求出的这个解加上 M 的整数倍仍然是合法解。那么如果要求的是最小正整数解,我们只需要把一开始求出的解对 M 取模就可以了。

long long CRT(long long M){
    long long sum=0,tmp,v;
    for (int i=1;i<=cnt;i++){
        tmp=M/m[i];
        v=getInv(tmp,m[i]);
        sum=(sum+tmp*a[i]*v)%M;
    }
    return sum;
}

扩展——模数不互质

如果同余方程组扩展到模数不互质的情况,这个时候首先不能用扩展欧几里得直接求逆元,并且还有可能会出现无解的情况。这个时候就不能用上面那种方法求解了,我们需要把方程两两合并化简。

设有两个方程 xa1(mod m1) xa2(mod m2) 。把这两个方程展开写成 x=a1+n1×m1 x=a2+n2×m2

这两个方程左边都是 x ,把它们两个联立就变成了 n1×m1+a1=n2×m2+a2 。再移项化简一下就得到了 n1×m1n2×m2=a2a1

这又是一个形如 ax+by=c 的不定方程。结合扩展欧几里得算法可以判断如果 (a2a1) mod gcd(m1,m2)0 的话这个同余方程组就是无解的。设 gcd(m1,m2)=d ,我们可以用扩展欧几里得求出 n1 在模 m2d 意义下的值 N 。但这显然不一定是真正的 n1 ,所以我们把 n1 写作 y×m2d+N

n1 代入第一个方程 x=a1+n1×m1 就可以得到 x=a1+(y×m2d+N)×m1 ,把括号展开就可以得到 x=a1+N×m1+y×m1×m2d

这里y虽然是一个未知数,但我们可以把这个方程写作同余方程的形式,就是 xa1+N×m1(mod m1×m2d) 。那么这两个方程就合并成了一个方程,得到了新的余数和新的模数。那么我们就可以把给出的所有方程两两合并成新的方程,最后只剩下一个方程的时候当然就可以很方便地求解了。

bool merge(long long &a1,long long &m1,long long a2,long long m2){
    long long c,d,x,a3,m3;
    c=a2-a1;d=gcd(m1,m2);
    if (c%d!=0) return false;
    c=c/d;m1=m1/d;m2=m2/d;
    x=getinv(m1,m2);
    x=(x*c)%m2;
    x=x*(m1*d)+a1; 
    m3=m1*m2*d;
    a3=(x%m3+m3)%m3;
    a1=a3;m1=m3;
    return true;
}
long long CRT(){
    long long A=a[1],M=r[1];
    for (int i=2;i<=n;i++)
      if (!merge(A,M,a[i],r[i]))
        return -1;
    return (A%M+M)%M;
}

应用

中国剩余定理最常用的版本还是模数互质的版本,因为有些题目中我们为了利用某些性质必须保证模数是质数或模数与什么东西互质,而如果它偏偏给了一个不是质数的模数,我们就可以把这个模数分解质因数,对每个质因数求出结果以后再用中国剩余定理合并。这个时候显然模数们都是互质的,用第一种形式就可以了。

简单的练习题:

  1. POJ-1006 Biorhythms
  2. POJ-2891 Strange Way to Express Integers
  3. HDU-1573 X问题

你可能感兴趣的:(板子们,烧脑的数论)