解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是传值而不是传引用的)。但有时候可以把每个同余方程分解成几个小的。