解k个线性同余方程构成的线性同余方程组,每个方程形如: $x_i = c_i mod m_i$ ,假如有解输出最小非负整数解,否则输出-1。
可能在模数比较大的时候会溢出的版本,方法是直接对两个方程强行合并。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXK = 100005;
void exgcd(ll a, ll b, ll &x, ll &y) {
if(!b)
x = 1, y = 0;
else
exgcd(b, a % b, y, x), y -= a / b * x;
}
ll inv(ll a, ll b) {
ll x = 0, y = 0;
exgcd(a, b, x, y);
x = (x % b + b) % b;
if(!x)
x += b;
return x;
}
int k;
ll c[MAXK], m[MAXK];
//解k个线性同余方程构成的方程组, xi = ci mod mi ,假如有解,返回最小非负整数解,否则返回-1
ll exCRT(int k) {
ll c1, c2, m1, m2, t;
for(int i = 2; i <= k; ++i) {
m1 = m[i - 1], m2 = m[i], c1 = c[i - 1], c2 = c[i];
t = __gcd(m1, m2);
if((c2 - c1) % t != 0)
return -1;
m[i] = m1 * m2 / t;
c[i] = inv(m1 / t, m2 / t) * ((c2 - c1) / t) % (m2 / t) * m1 + c1;
c[i] = (c[i] % m[i] + m[i]) % m[i];
}
return c[k];
}
//解k个线性同余方程构成的方程组, xi = ci mod mi ,假如有解,返回最小非负整数解,否则返回-1
int main() {
#ifdef Inko
freopen("Inko.in", "r", stdin);
#endif // Inko
while(~scanf("%d", &k)) {
for(int i = 1; i <= k; ++i)
scanf("%lld%lld", &m[i], &c[i]);
printf("%lld\n", exCRT(k));
}
}
有时候题目的确会溢出,这个时候要选择带有大数取模的Java(注意BigInteger是传值而不是传引用的)。但有时候可以把每个同余方程分解成几个小的。