8.15 数论 线性同余方程组(crt 与 excrt
crt:
问题:
求解形如:
的方程组
构造思想求解:
构造解 X = x1 + x2 + x3 +......+xn
其中 xi 的构造为 bi * (逆元项)*(消参项)
消参项: 使得 除了xi的其他项xj mod ai == 0
逆元项: 为消参项在mod ai 意义下的逆元 ,使得 bi * (逆元项)*(消参项)mod ai = bi mod ai
则: $$X\equiv b_{i}\ (mod\ a_{i} ) \ , 1\leq i\leq 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本身不会被消了
但这种情况下,模数不互质的同余方程的解是有重叠的,可以用扩展欧几里得定理和线性同余方程的性质进行合并,详见:
excrt = 用exgcd 求 crt
一道例题:
Strange Way to Express Integers
excrt的模板题
将同余方程组用exgcd合并成一个方程组求解
合并过程:
则:
$$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\n" ,a[n]%b[n] ); } return 0; }