中国剩余定理

中国剩余定理

求解模线性方程组。

\[\begin{aligned} \left\{ \begin{array}{l} x_1\equiv a_1\pmod{r_1} \\ x_2\equiv a_2\pmod{r_2} \\ \cdots\\ x_k\equiv a_k\pmod{r_k} \\ \end{array} \right. \end{aligned} \]

crt

\(r_1,r_2,\cdots,r_k\) 为互质的,设 \(M=\prod_{i=1}^k r_i,A_i=\dfrac{M}{r_i}\) ,可的 \(M\)\(r_1,\cdots,r_k\) 的 lcm

可得 \(A_i,r_i\) 是互质的,则必存在 \(A_i\)\(r_i\) 意义下的逆元 \(t_i\) 使得 \(A_it_i\equiv 1\pmod{r_i}\)

\(x_i=a_iA_it_i\) 是满足第 \(i\) 个方程的。同时 \(\forall j\ne i\) ,都有 \(x_i\equiv 0\pmod{r_j}\) ,因为 \(A_i\) 中有一个因子 \(r_j\)

可以得到通解 \(x=\sum_{i=1}^k a_i A_i t_i+pM(p\in\mathbb{Z})\) 。显然满足条件

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long uLL;
typedef long double LD;
typedef long long LL;
typedef double db;
const int N = 55;
LL exgcd(LL a, LL b, LL &x, LL &y) {
    if (!b) return x = 1, y = 0, a;
    register LL gcd = exgcd(b, a % b, y, x);
    return y -= a / b * x, gcd;
}
inline LL inv(LL a, LL b) {
    register LL x, y;
    exgcd(a, b, x, y);
    return (x % b + b) % b;
}
int n;
LL a[N], r[N], mul = 1, A[N], ans;
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%lld%lld", &r[i], &a[i]), mul *= r[i];
    for (int i = 1; i <= n; i++) {
        A[i] = mul / r[i];
        ans += a[i] * A[i] * inv(A[i], r[i]);
    }
    printf("%lld", ans % mul);
}

excrt

\(r_1,\cdots,r_k\) 不互质,此时使用扩展 crt

\[\begin{aligned} \left\{ \begin{array}{l} x_1\equiv a_1\pmod{r_1} \\ x_2\equiv a_2\pmod{r_2} \\ \cdots\\ x_k\equiv a_k\pmod{r_k} \\ \end{array} \right. \end{aligned} \]

看前两个方程。

\(x=k\cdot r_1+a_1=p\cdot r_2+a_2\) ,其中 \(k,p\in\mathbb{Z}\)

\(k\cdot r_1-p\cdot r_2=a_2-a_1\) ,这是形如 \(ax+by=c\) 的形式,用 exgcd 求解。

此处可得 \(\gcd(r_1,r_2)\nmid (a_2-a_1)\) 时无解。

求出一个解 \(k_0\) ,则通解 \(k=k_0+o*\dfrac{r_2}{\gcd(r_1,r_2)}(o\in\mathbb{Z})\)

\(k\) 带入 \(k\cdot r_1+a_1\) 可得

\[(k_0+o*\dfrac{r_2}{\gcd(r_1,r_2)}) \cdot r_1+a_1=k_0*r_1+a_1+o*\text{lcm}(r_1,r_2) \]

\(x\equiv k_0*r_1+a_1\pmod{\text{lcm}(r_1,r_2)}\)

相当于合并了两个方程,合并 \(n-1\) 次即可得到答案。

注意 \(k_0\) 应乘上 \(\dfrac{a_2-a_1}{\gcd(r_1,r_2)}\)

模板题需要用龟速乘。

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long uLL;
typedef long double LD;
typedef long long LL;
typedef double db;
const int N = 100005;
LL exgcd(LL a, LL b, LL &x, LL &y) {
    if (!b) return x = 1, y = 0, a;
    register LL gcd = exgcd(b, a % b, y, x);
    return y -= a / b * x, gcd;
}
LL G;
inline LL mul(LL x, LL y, LL p) {
    return (x * y - (LL)((LD)x / p * y) * p + p) % p;
}
inline bool sol(LL a, LL b, LL c, LL &x, LL &y) {
    G = exgcd(a, b, x, y);
    register LL t;
    if (c % G) return false;
    t = b / G;
    x = mul(x, c / G, t);
    x = (x + t) % t;
    y = (c - a * x) / b;
    return true;
}
int n, isL;
LL a[N], r[N];
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%lld%lld", &r[i], &a[i]);
    isL = 1;
    for (int i = 1; i < n; i++) {
        register LL x, y;
        if (!sol(r[i], r[i + 1], a[i + 1] - a[i], x, y)) {
            isL = 0; break;
        }
        a[i + 1] = x * r[i] + a[i];
        r[i + 1] = r[i] / G * r[i + 1];
    }
    printf("%lld", a[n]);
}
posted @ 2022-08-08 21:46  小蒟蒻laf  阅读(36)  评论(0编辑  收藏  举报