问题一:给你n个方程组:
x%m[0] = a[0]
x%m[1] = a[1]
···
x%m[n-1] = a[n-1]
求变量x 的值,其中m[]必须两两互质。
直接exgcd可以KO
LL gcd(LL a, LL b){ return b == 0 ? a : gcd(b, a%b); } void exgcd(LL a, LL b, LL &d, LL &x, LL &y) { if(!b){d = a, x = 1, y = 0;} else { exgcd(b, a%b, d, y, x); y -= x * (a / b); } } //l-r为方程组下标 方程 x % m = a LL CRT(LL l, LL r, LL *m, LL *a) { LL M = 1, d, y, x = 0; for(LL i = l; i <= r; i++) M = M / gcd(M, m[i]) * m[i]; for(LL i = l; i <= r; i++) { LL w = M / m[i]; exgcd(m[i], w, d, d, y); x = (x + y * w * a[i]) % M; } return (x + M) % M; }
问题二:给你n个方程组:
x%m[0] = a[0]
x%m[1] = a[1]
···
x%m[n-1] = a[n-1]
求变量x 的值,无解输出-1。其中m[]的值没有要求。
这时需要用到合并方程的做法。因为exgcd只能求解一个方程a * x + b * y = c的解,我们需要把n个方程合并成一个。
合并方程的讲解如下:来自九野的博客
问题描述:给出bi,ni的值,且n1, n2, n3,…, ni两两之间不一定互质,求Res的值?
解:采用的是合并方程的做法。
这里将以合并第一第二个方程为例进行说明
由上图前2个方程得(设k1、k2为某一整数):
所以我们简化一下结论:
已知方程组(b1,b2,n1,n2是已知量):
res%b1 = n1
res%b2 = n2
->
合并两条方程得到:
res % ( (n1*n2)/d ) = b1+n1*( K%(n2/d))
其中k = (k1*(b2-b1)/d) % (n2/d);
其中d = gcd(n1,n2);
其中k1:
k1*n1 - k2*n2 = b2-b1
k1, d 可以直接由extend_gcd得到 extend_gcd(n1,n2,d,k1,k2);
(b2-b1)%d == 0 说明extend跑出的k1是一个解,否则说明不存在满足解的k1
注意求k时:为了得到最小非负整数k,所以用一个取模的技巧
k = (k%mod+mod)%mod, 其中mod = m[i] / d,每一次的mod值可能不一样;
实现:
LL gcd(LL a, LL b){ return b == 0 ? a : gcd(b, a%b); } void exgcd(LL a, LL b, LL &d, LL &x, LL &y) { if(!b){d = a, x = 1, y = 0;} else { exgcd(b, a%b, d, y, x); y -= x * (a / b); } } LL CRT(LL l, LL r, LL *m, LL *a)//方程 x % m = a { LL lcm = 1; for(LL i = l; i <= r; i++) lcm = lcm / gcd(lcm, m[i]) * m[i]; for(LL i = l+1; i <= r; i++)//合并方程 共需r-l+1次 { LL A = m[l], B = m[i], d, x, y, c = a[i] - a[l];//合并方程后 各个系数 exgcd(A, B, d, x, y);//d = gcd(A, B); if(c % d)//对扩展后的式子a*x + b*y = c 当且仅当c % d == 0是才有解 return -1; LL mod = m[i] / d; LL k = ((x * c / d) % mod + mod) % mod; a[l] = m[l] * k + a[l]; m[l] = m[l] * m[i] / d;//更新方程系数 利用该方程继续和下一个方程合并 } if(a[l] == 0) return lcm;//特判 return a[l];//合并到最后a[l] 就是解 }