8.15 数论 线性同余方程组(crt 与 excrt

crt:

问题:

求解形如:

8.15 数论 线性同余方程组(crt 与 excrt

的方程组

构造思想求解:

构造解 X = x1 + x2 + x3 +......+xn

其中 xi 的构造为  bi * (逆元项)*(消参项)

消参项: 使得 除了xi的其他项xj mod ai == 0

逆元项: 为消参项在mod ai 意义下的逆元 ,使得 bi * (逆元项)*(消参项)mod ai = bi mod ai

则: $$Xequiv b_{i}  (mod  a_{i} ) , 1leq ileq  n $$ ,即满足要求.

不难想到消参项的构造 :

                     $$M_{i} =  prod_{1}^{n}a / a_{i}$$

又 ,对于通解X,任何    $ X + n*prod_{1}^{n}a  $ 都是可行解,

所以 ,令 $ t = prod_{1}^{n}a / a_{i} $ , 则最小正整数解为 $(X; mod ;t + t ) ;mod; t$

伪代码:

1 → n
0 → ans
for i = 1 to k
    n * n[i] → n
for i = 1 to k
    n / n[i] → m
    inv(m, n[i]) → b               // b * m mod n[i] = 1
    (ans + m * b) mod n → ans
return (ans mod t + t ) mod t

excrt:

即上述方程中ai不互质的情况 ,这样的话消参项的构造就不能保证ai本身不会被消了

但这种情况下,模数不互质的同余方程的解是有重叠的,可以用扩展欧几里得定理和线性同余方程的性质进行合并,详见:

https://oi-wiki.org/math/crt/

 excrt = 用exgcd 求 crt

一道例题: 

Strange Way to Express Integers

excrt的模板题

将同余方程组用exgcd合并成一个方程组求解

合并过程:

8.15 数论 线性同余方程组(crt 与 excrt

则:

$$x = t_{1}*b_{1} + a_{1} = t_{2}*b_{2} + a_{2} $$

有:

$$t_{1}*b_{1}  - t_{2}*b_{2} = a_{2}- a_{1}$$

此方程可解,解出最小正整数解$t_{1}$ 或 $t_{2}$代入原方程组就可得合并后的方程,

模数变成$lcm( b_{1} , b_{2})$,(因为同时能被$b_{1} , b_{2}$模得$x$);

1.结果会很大,要用ll ,注意多组数据

2.puts( )出的结果自带空行

代码:

// excrt --- 用exgcd 求 crt
#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 500050;
ll ex_gcd( ll a ,ll b ,ll &x ,ll &y ){
      if( b==0 ){
         x = 1;
         y = 0;
        return a;
      }
      ll d = ex_gcd( b ,a%b , x ,y );
      ll tmp = x;
      x = y;
      y = tmp - a/b * y;
      return d;
}

bool equ( ll a ,ll b ,ll c ,ll &x ,ll &y ,ll &g){
     g = ex_gcd( a ,b ,x ,y );
     if( c%g ) return 0;
     ll k = c/g ,t = b/g;
     x *= k;
     y *= k;
     x = (x % t + t)%t;
     return 1;
}

ll n;
ll a[N] ,b[N];

int main( ){
     while( ~scanf("%lld" ,&n) ){
             int flag  = 0;
             for( int i =1 ;i<=n ;i++ ){
                 scanf("%lld%lld" ,b+i ,a+i);
             }
             ll ans = 0 ,x ,y ,g;

             for( int i = 1 ;i<n ;i++ ){
                 if( equ( b[i] ,b[i+1] ,a[i+1] - a[i] ,x ,y ,g) ){
                     a[i+1] = x*b[i] + a[i];
                     b[i+1] = b[i]/g*b[i+1];
                 }
                 else {
                     puts( "-1" );
                     flag = 1;
                     break;
                 }
             }
             if( flag )continue;
             printf( "%lld
" ,a[n]%b[n] );
        }
     return 0;
}