给定模线性方程组,求最终的值的通解。点击
两个模方程可以化解成一个模方程
x mod a1 = b1
x mod a2 = b2
a1*k1 + a2*k2 = b2 – b1 // 其中k1k2是自由元
用扩展欧几里得算出k1的解,当然它是一个解系,找出最小k1作为特解,带入x = a1 * k1 + b1得到x
然后把这个x当作特解,记作x1.
之前k1本来是一个解系,他的模是a2/gcd(a1,a2),[简单将gcd(a1,a2)记作t], 也就是说k1如果作为一个解系的话,k1 = a2/t * x + k0
其中x是任意整数,k0是一个特解。
将求到的k1代入到x = a1 * k1 + b1 中,得到的是一个x的解系,如果用上边的特解x0表示,就是x = x0 + a1*a2/t;
得到一个新的模方程 x mod a1*a2/t = x0 ,这个方程继承上面两个方程的特性,所以两者之间是等价的。
这样将两个两个方程消掉,最后只剩下一个方程,最后的解系就是最终的x的解系。如果中间出现欧几里得算不出来解,那么说明这个方程组没有解。
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <iostream> using namespace std; typedef long long LL; const int N = 1e6 + 500; void extend_gcd(LL a, LL b, LL &x, LL &y, LL &d) { if(b == 0) { x = 1; y = 0; d = a; } else { extend_gcd(b, a%b, y, x, d); y -= (a/b) * x; } } int T,n,m; LL a[20],b[20]; int main() { scanf("%d", &T); while(T--) { scanf("%d%d", &n, &m); for(int i = 0; i < m; i++) scanf("%d", &a[i]); for(int j = 0; j < m; j++) scanf("%d", &b[j]); LL flag = 0; LL x, y, d; LL m1 = a[0], r1 = b[0]; LL m2, r2; LL c, t; for(int i = 1; i < m; i++) { m2 = a[i], r2 = b[i]; extend_gcd(m1, m2, x, y, d);//d = gcd(m1,m2) m1*x + m2*y = gcd(m1,m2) c = r2 - r1; if(c % d) { flag = 1; break; } t = m2 / d; // m1*(x+m2/d*k1) + m2*(y + m1/d*k2) = m1*x + m2*y + (m1*m2/d * k1 + m2*m1/d*k2) = gcd(m1,m2) x = (((x * c / d) % t) + t) % t; r1 = m1*x + r1; m1 = m1*m2/d; r1 %= m1; } int ans = 0; if(flag || r1 > n) { printf("0\n"); } else { int ans = (n - r1) / m1 + 1; if(r1 == 0) ans--; printf("%d\n", ans); } } return 0; }
时间: 2024-10-11 06:23:26