poj 2891 Strange Way to Express Integers (扩展gcd)
题意:给k对数,每对ai, ri。求一个最小的m值,令m%ai = ri;
分析:由于ai并不是两两互质的, 所以不能用中国剩余定理。
只能两个两个的求。
a1*x+r1=m=a2*y+r2
联立得:a1*x-a2*y=r2-r1;
设r=r2-r2;
互质的模线性方程组m=r[i](mod a[i])。两个方程可以合并为一个,新的a1为lcm(a1,a2),
新的r为关于当前两个方程的解m,然后再和下一个方程合并……。(r2-r1)不能被gcd(a1,a2)整除时无解。 怎么推出的看了好多博客也没有介绍。
下面求最小的解的时候有一个定理,上一篇博客青蛙 也用到了: 对方程 ax ≡ b (mod) n
d = gcd(a,n) 若 d | b 则 有 d 个解 ,最小的解为x0 = (x*(b/d) mod n + n )mod(n/d)
则所有满足方程 x = x0 (mod) (n/d) 的 x 都是原方程的解。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #define LL long long 6 using namespace std; 7 8 void exgcd(LL a, LL b, LL &d, LL &x, LL &y) 9 { 10 if(!b) {d = a; x = 1; y = 0;} 11 else{ exgcd(b, a%b, d, y, x); y -= x*(a/b); } 12 } 13 int main() 14 { 15 int flag; 16 LL k, a1, a2, r1, r2; 17 LL c, d, x, y, q; 18 while(~scanf("%lld", &k)) 19 { 20 flag = 0; 21 scanf("%lld%lld", &a1, &r1); 22 k--; 23 while(k--) 24 { 25 scanf("%lld%lld", &a2, &r2); 26 if(flag) 27 continue; 28 c = r2-r1; 29 exgcd(a1, a2, d, x, y); 30 if(c%d!=0) 31 flag = 1; 32 q = a2/d; 33 x = (x*(c/d)%q + q)%q; //求最小的解 34 r1 = x*a1 + r1; //令r1为所求值,x; 35 a1 = a1*a2/d; //令a1为a1a2最小公倍数 36 } 37 if(flag) 38 printf("-1\n"); 39 else 40 printf("%lld\n", r1); 41 } 42 return 0; 43 }