POJ 2891 扩展欧几里德
这个题乍一看跟剩余定理似的,但是它不满足两两互素的条件,所以不能用剩余定理,也是给了一组同余方程,找出一个X满足这些方程,如果找不到的话就输出-1
因为它不满足互素的条件,所以两个两个的合并,最后合成一个。
题目给定的是
M % m1 = r1
M % m2 = r2
......
M % mn = rn
只需将两个式子合并成一个式子,那么这个合并的这个式子就可以继续和下面的式子继续合并,知道合到最后一个式子。
首先来看下两个式子怎么合并。
M % m1 = r1 可以写成 M = k1 * m1 + r1;
同理M = k2 * m2 + r2
所以k1 * m1 + r1 = k2 * m2 + r2
k1 * m1 - k2 * m2 = r2 - r1
将k1 看成x, m1看成a, k2看成y, m2看成b, r2 - r1看成c
那么这个式子就变成了
ax-by=c;不定方程
这时候就可以用不定方程来解了。
因为题目给的两两可能不互素,所以gcd(a, b) 有可能不等于1,所以,这个方程不一定有解,所以就是判断这个题有没有解的条件。
如果有解的话,用扩展欧几里德可以的出这个方程的解,然后将x带入到 k1 * m1 + r1中得到M,这时候M就是这两个方程组的一个特解,通解就是M' = M + K * LCM(m1, m2);
这个式子可以变化成M' % LCM(m1, m2) = M;所以又可以继续跟下面的式子合并了。所以这个式子对应的余数r就是M, 模数(就是式子中的mi)就是LCM(m1,m2);
到这里之后就合并好了一个式子。下面就是继续按照这个过程合并到第n个式子了。最后合并完之后就剩下一个式子,求出来的M就是方程组的特解,通解为M' = M + k * LCM; 其中LCM = LCM(m1, m2...mn), 最小正整数解为(M % LCM + LCM) % LCM;
还有一个坑就是如果边输入边处理的话,不要找到无解立马就跳出循环,因为后面的数据还没输入完。
代码如下:
/************************************************************************* > File Name: 5.cpp > Author: Howe Young > Mail: 1013410795@qq.com > Created Time: 2015年08月01日 星期六 15时39分47秒 ************************************************************************/ #include <iostream> #include <cstdio> using namespace std; const int maxn = 1e5 + 5; typedef long long ll; ll m[maxn], r[maxn]; ll exgcd(ll a, ll b, ll &x, ll &y) { if (b == 0) { x = 1; y = 0; return a; } ll r = exgcd(b, a % b, x, y); ll t = x; x = y; y = t - a / b * y; return r; } int main() { int k; while (~scanf("%d", &k)) { for (int i = 1; i <= k; i++) scanf("%lld %lld", &m[i], &r[i]); ll m1, m2, r1, r2, c, d; m1 = m[1]; r1 = r[1]; bool flag = true; for (int i = 2; i <= k; i++) { ll x, y; d = exgcd(m1, m[i], x, y); if ((r[i] - r1) % d != 0) { flag = false; break; } c = r[i] - r1; x = c / d * x % (m[i] / d); r1 = m1 * x + r1; m1 = m1 / d * m[i]; r1 %= m1; } if (!flag) { printf("-1\n"); continue; } r1 = (r1 + m1) % m1; printf("%lld\n", r1); } return 0; }