模板 - 扩展中国剩余定理

流过昼夜 提交于 2019-11-27 22:41:38

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!