CRT和EXCRT

CRT

简介

CRT(中国剩余定理)可用于求解如下形式的线性同余方程组

\[\begin{cases} x \equiv b_1 \pmod{a_1}\\ x \equiv b_2 \pmod{a_2}\\ \cdots \\ x \equiv b_n \pmod{a_n} \end{cases} \]

其中 \(a_1,a_2,\cdots,a_n\) 两两互质

流程

计算所有模数的积 \(M = \prod_{i=1}^n a_i\)

对于第 \(i\) 个方程,计算 \(m_i=\frac{M}{a_i}\)

由于模数两两互质,所以 \(m_i\) 在模 \(a_i\) 的意义下存在逆元

计算 \(m_i\) 在模 \(a_i\) 意义下的逆元 \(m_i^{-1}\) ,计算 \(c_i=m_i\cdot m_i^{-1}\)

可得方程的一个解 \(x_0\) 为:

\[x_0=\sum_{i=1}^n b_i c_i \]

方程的通解 \(x\) 为:

\[x=x_0+k\times M \]

方程的最小正整数解 \(x_1\) 为:

\[x_1=(x_0\bmod M+M)\bmod M \]

时间复杂度 \(O(n\log n)\)

证明

我们需要验证, \(\forall i,x_0 \equiv b_i\pmod{a_i}\)

\[\begin{aligned} x_0 &\equiv b_1c_1+b_2c_2+\cdots+b_nc_n &\pmod{a_i}\\ &\equiv b_ic_i &\pmod{a_i}\\ &\equiv b_im_im_i^{-1} &\pmod{a_i}\\ &\equiv b_i &\pmod{a_i} \end{aligned} \]

代码

Luogu P1495 中国剩余定理

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int n;
int a[15], b[15];

ll exgcd(ll a, ll b, ll & x, ll & y)
{
    if(b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    ll d = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}

ll inverse(ll a, ll b)
{
    ll x, y;
    exgcd(a, b, x, y);
    return (x % b + b) % b;
}

ll CRT()
{
    ll M = 1, ans = 0;
    for(int i = 1; i <= n; i++)
        M *= a[i];
    for(int i = 1; i <= n; i++) {
        ll m = M / a[i];
        ans = (ans + b[i] * inverse(m, a[i]) % M * m % M) % M;
    }
    return ans;
}

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++){
        scanf("%d%d", &a[i], &b[i]);
    } 
    printf("%lld\n", CRT());
    return 0;
}

EXCRT

简介

CRT可求解模数两两互质的同余方程组,而失去模数两两互质这一条件后,就无法求出逆元,从而无法像上面那样构造出符合条件的解,EXCRT可以解决这一问题

原理

我们先考虑含有两个同余方程的方程组如何求解:

\[\begin{cases} x \equiv b_1 \pmod{a_1}\\ x \equiv b_2 \pmod{a_2} \end{cases} \]

可以把条件写成:

\[x=b_1+k_1a_1=b_2+k_2a_2 \]

移项得:

\[k_1a_1-k_2a_2=b_2-b_1 \]

这样就变成了一个新的同余方程,由裴蜀定理可知 \(\gcd(a_1,a_2)\not\mid b_2-b_1\) 时无解,若解存在,可以用EXGCD求解

\(k_1=k\)\(k_1a_1-k_2a_2=\gcd(a_1,a_2)\) 的一个解,则原方程的一个解为:

\[k_1=\frac{k(b_2-b_1)}{\gcd(a_1,a_2)} \]

带回条件得:

\[x=b_1+\frac{k(b_2-b_1)}{\gcd(a_1,a_2)}a_1 \]

所以原先的两个方程可合并为:

\[x\equiv b_1+\frac{k(b_2-b_1)}{\gcd(a_1,a_2)}a_1 \pmod{\text{lcm}(a_1,a_2)} \]

这样就得到了方程组的解,对于包含更多同余方程的方程组,按照这样的方法逐个合并到只剩最后一个即可

时间复杂度 \(O(n\log n)\)

代码

Luogu P4777 扩展中国剩余定理

由于本题数据大小超过了 long long 的范围,所以用 __int128 ,而 __int128 无法用 cout\cin 进行输入输出,所以先用 long long 再强制转换,而运算过程中全部使用 __int128

#include<bits/stdc++.h>
#define ll __int128
using namespace std;

int n;
ll a1, b1, a2, b2;

ll exgcd(ll a, ll b, ll & x, ll & y)
{
    if(b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    ll d = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}

void merge()
{
    ll k, y, d;
    d = exgcd(a1, a2, k, y);
    ll t = a2 / d;
    k = (k % t + t) % t;
    ll mod = a1 / d * a2;
    b1 = ((b1 + a1 / d * k * (b2 - b1)) % mod + mod) % mod;
    a1 = mod;
}

int main()
{
    scanf("%d", &n);
    long long a, b;
    scanf("%lld%lld", &a, &b);
    a1 = a, b1 = b;
    for(int i = 1; i < n; i++) {
        scanf("%lld%lld", &a, &b);
        a2 = a, b2 = b;
        merge();
    }
    printf("%lld\n", (long long)(b1 % a1));
    return 0;
}
posted @ 2021-12-29 17:14  f(k(t))  阅读(66)  评论(0编辑  收藏  举报