由扩展GCD梳理一文中我们知道了x的解为x0*(c/gcd(a,b)),x的最小范围为b/gcd(a,b)。
对于中国剩余定理,我们要做的是依次合并项。如:
r1+a1*k1=N
r2+a2*k2=N
合并为a1*k1-a2*k2=r2-r1
令a1=a , k1=x,a2=b,k2=y,r2-r1=r
有ax+by=r,按扩展GCD计算。
可以得到x的最优解。而有r1+a1*(x0+n*(b/gcd(a,b)))=N
化简后有:N=r1+a1*x0 + (a1*(b/gcd(a,b)))*n
令r1=r1+a1*x0 a1=a1*(b/gcd(a,b)
可以将其看为又一个N=r1+a1*k1。这样第一个式子就合并完成了。
之后再依次合并即可。
注:可能会有r1=0的情况,该情况下,说明所有式子的余数都是0。所以该种情况下,求所有数的最小公倍数即是解。
下面附题一个:
链接:https://www.nowcoder.net/acm/contest/75/B
来源:牛客网
题目描述
问题:给你k对a和r是否存在一个正整数x使每队a和r都满足:x mod a=r,求最小正解x或无解。
输入描述:
第一行是正整数k(k<=100000) 接下来k行,每行有俩个正整数a,r(100000>a>r>=0)
输出描述:
在每个测试用例输出非负整数m,占一行。 如果有多个可能的值,输出最小的值。 如果没有可能的值,则输出-1。
输入
2 8 7 11 9
输出
31
#include <iostream> using namespace std; //x mod a = r long long gcd(long long a, long long b) { if (b == 0) return a; return a%b == 0 ? b : gcd(b, a%b); } long long ExGcd(long long a, long long b, long long &x, long long &y) { if (b == 0) { x = 1; y = 0; return a; } long long t = ExGcd(b, a%b, x, y); long long temp = x; x = y;//x1=y2 y = temp - (a / b)*y; return t; } long long A[100000]; long long R[100000]; long long k; long long china_remain() { long long temp = 1; long long a = A[0], r1 = R[0]; while (temp != k) { //依次合并 long long b = A[temp]; long long r = R[temp] - r1; long long x, y; long long g = ExGcd(a, b, x, y); if (r%g != 0) { return -1; } x = x*(r / g); long long t = b / g; x = (x%t + t) % t;//缩范围,获得最小合法值 r1 = r1 + a*x; a = a*t; temp++; } if (r1 == 0)//此情况下,所有项的余数为0,则求这些数的最小公倍数就行 { r1 = 1; for (long long i = 0; i < k; i++) { r1 = r1*A[i] / gcd(r1, A[i]); } } return r1; } //思路:将多个式子合并为一个即可 int main() { cin >> k; for (long long i = 0; i < k; i++) cin >> A[i] >> R[i]; cout << china_remain() << endl; system("pause"); return 0; }
1万+

被折叠的 条评论
为什么被折叠?



