中国剩余定理学习笔记

前置知识:

  • 对于同余方程有了解和余数的各种性质很熟悉。

  • 学过求逆元

中国剩余定理,英文 Chinese Remainder Theorem,简称 CRT

中国剩余定理主要用来解决如下的同余方程:

{x≡a1( mod b1)x≡a2( mod b2)x≡a3( mod b3)⋅⋅⋅x≡an( mod bn) \left\{ \begin{matrix} x \equiv a_1(\bmod b_1)\\ x \equiv a_2(\bmod b_2)\\ x \equiv a_3(\bmod b_3)\\ ···\\ x \equiv a_n(\bmod b_n)\\ \end{matrix} \right. xa1(modb1)xa2(modb2)xa3(modb3)⋅⋅⋅xan(modbn)

其中,bib_ibi 两两互质。否则就不能使用中国剩余定理来解决。

可以发现,xxx 的答案可以有无穷多个。如果 ansansans 为最小的同时满足所有同余方程的 xxx 值,则 ans+∏i=1nbi×kans + \prod^{n}_{i=1} b_i \times kans+i=1nbi×k 也是一个满足要求的答案,kkk 可以为任意的数。

所以,中国剩余定理是用来求解 ansansans 值。


我们不妨将所有的余数分拆,根据余数的可加性,我们就可以将同余方程拆成几个子同余方程:

可以注意到这里一共有 nnn 个同余方程,我们依次求出这 nnn 个同余方程的解:ans1,ans2,⋅⋅⋅,ansnans_1 , ans_2 , ··· , ans_nans1,ans2,⋅⋅⋅,ansn

将这 nnn 个答案加起来,再模上 ∏i=1nbi\prod^{n}_{i=1} b_ii=1nbi。因为所有的解都是同余的,我们也可以使用任意一个解求出最小的解。


但是这样子还是有一些难以计算,不妨再化简一下:

我们依次求出来这几个方程的解:as1,as2,⋅⋅⋅,asnas_1,as_2,···,as_nas1,as2,⋅⋅⋅,asn

则根据余数的可乘性,∀i,ansi=asi×ai\forall i,ans_i = as_i \times a_ii,ansi=asi×ai

然后就可以得出答案。


注意到所有的方程其实都是差不多的:对于一个 bib_ibi 取余为 111,对于剩余的 bib_ibi 都是取余为 000。不妨针对其中一个方程来看,方便起见,就选第一个方程。

这时候,我们的问题转化为了求

{x≡1( mod b1)x≡0( mod b2)x≡0( mod b3)⋅⋅⋅x≡0( mod bn) \left\{ \begin{matrix} x \equiv 1(\bmod b_1)\\ x \equiv 0(\bmod b_2)\\ x \equiv 0(\bmod b_3)\\ ···\\ x \equiv 0(\bmod b_n)\\ \end{matrix} \right. x1(modb1)x0(modb2)x0(modb3)⋅⋅⋅x0(modbn)

这个方程的解。

不妨针对余数为 000 的部分合并一下。

使用 m1m_1m1 记录除了 b1b_1b1 以外剩余 bib_ibi 的乘积。

所以就转化成了解决这个方程:

{x≡1( mod b1)x≡0( mod m1) \left\{ \begin{matrix} x \equiv 1(\bmod b_1)\\ x \equiv 0(\bmod m_1)\\ \end{matrix} \right. {x1(modb1)x0(modm1)

则对于答案 asasas,有 as=m1×k=b1×l+1as = m_1 \times k = b_1 \times l + 1as=m1×k=b1×l+1

针对子等式 m1×k=b1×l+1m_1 \times k = b_1 \times l + 1m1×k=b1×l+1,为了把其中一个恶心的 lll 弄掉,不妨转化为同余式 m1×k≡1( mod b1)m_1 \times k \equiv 1(\bmod b_1)m1×k1(modb1)

然后 m1m_1m1b1b_1b1 都是可以求出来的定值。

然后你发现了什么???发现,kkk 这东西实际上就是b1b_1b1 为模数,m1m_1m1 的逆元,即 m−1m^{-1}m1

然后???你就可以使用扩展欧几里得做这道题了。

至于 m1m_1m1 怎么求,只需要正难则反,使用全体乘积除以 b1b_1b1 即可。

其他的如法炮制。


模板题:P1495 【模板】中国剩余定理(CRT)/ 曹冲养猪

提示:这道题需要开 __int128!!!

但是 __int128 是没有系统自定义的读入函数的,需要自己手写快读。

这里的 a,ba,ba,b 的位置在输入中反了,注意看清楚题意。

// 使用中国剩余定理(CRT)求解线性同余方程组的代码
#include <bits/stdc++.h>  // 包含常用库
#define int __int128      // 使用128位整数类型处理大数
using namespace std;

int n;                    // 同余方程的数量
const int N = 20;         // 方程最大数量
int a[N], b[N];           // a[i]存储模数,b[i]存储余数
int m = 1;                // 所有模数的乘积(中国剩余定理中的M)

// 读取128位整数(处理输入)
inline int read() {
    char c = getchar();
    int x = 0, s = 1;
    while (c < '0' || c > '9') {
        if (c == '-') s = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9') {
        x = x * 10 + c - '0';
        c = getchar();
    }
    return x * s;
}

// 输出128位整数(处理输出)
void write(int x) {
    if (x > 9) write(x / 10);  // 递归分解数字
    putchar(x % 10 | 48);      // 输出各位字符
}

// 扩展欧几里得算法:解ax + by = gcd(a,b),返回gcd,x,y为解
int exgcd(int a, int b, int &x, int &y) {
    if (!b) {
        x = 1, y = 0;
        return a;
    }
    int r = exgcd(b, a % b, x, y);
    int t = x;
    x = y;                      // 更新x
    y = t - a / b * y;          // 更新y
    return r;
}

signed main() {
    n = read();                  // 读取方程数量
    for (int i = 1; i <= n; i++) {
        a[i] = read(), b[i] = read();  // 读取每个方程的模数a和余数b
        m *= a[i];               // 计算所有模数的乘积m
    }
    
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        int k = m / a[i];        // 计算mi = m / a_i
        int x, y;
        exgcd(k, a[i], x, y);    // 求mi模a_i的逆元x
        // 根据CRT累加解:ans += b_i * mi * inv(mi)
        ans = (ans + k * b[i] * x) % m; // 注意取模防止溢出
    }
    // 输出最小正整数解(处理负数情况)
    write((ans % m + m) % m);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值