首页 > 代码库 > poj 2891 Strange Way to Express Integers (扩展gcd)

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 }