[扩展中国剩余定理(EXCRT)]

前言

进入正题


题目描述

给定 $n$ 组 非负整数 $$a_i,b_i$$,求解关于 $x$ 的方程组

            $x ≡ a_1 (mod~b_1)$

            $x ≡ a_2 (mod~b_2)$

            $.$

            $.$

            $x ≡ a_n (mod~b_n)$

声明 : $M$ 表示 前 $i-1$ 个$b_i$ 的 最小公倍数$(LCM)$, $ans$ 表示 满足前 $i-1 $个方程的最小解

初始化: 特判 第一组方程 令 $M = b_1 , ans = a_1$(第一组的话 $b_1$的最小公倍数就是自己,只看第一个方程 $ans$ 就等于 $a_1$)

从第二组开始:

        $LCM(b_1,b_2) =  frac{b_1×b_2}{gcd(b_1,b_2)}$这个不需要证明

        因为 此时 $M = b_1$ ,所以原式可化为:

                 $LCM(M,b_2) =  frac{M×b_2}{gcd(M,b_2)}$

考虑: $ans + t×M$ 一定是 前 $i-1$ 个方程 的通解,因为 $ans$ 是 最小解 所以 $ans$ 加上前 $i-1$ 个方程 $b_i$ 的最小公倍数 一定是前 $i-1$ 个方程 的通解。(其中 $t ∈ Z$)

所以 如果要使得 $ans$ 也满足 下一个方程的话(以第二个 来举例):

    $ans + t×M ≡ a_2 (mod~b_2)$ 此时 $ans$ 是$a_1$ 是已知的

移项得 : $t×M ≡ a_2 - ans (mod~b_2)$(这一看就是 扩展欧几里得)($ax ≡ c (mod~b)$)

所以 此时 $t$ 就相当于是 $x$ ,$M$ 相当于是$a$ ,$a_2 - ans$ 就相当于是$c$

所以利用扩展欧几里得 可以 解得$t$

但是 我们 要考虑 一个问题 扩展欧几里得 的操作

也就是说 我们解得的$t$ 其实是

$t×M ≡ gcd(M,b_2)~(mod~b_2)$的解

所以 $a_2 - ans$ 到 $gcd(M,b_2)$ 相当于是乘了一个 $frac{gcd(M,b_2)}{a_2 - ans}$ 所以我们得到的解 也相当于是 乘了 一个$frac{gcd(M,b_2)}{a_2 - ans}$

那么真正的 t 应该是 $frac{t}{frac{gcd(M,b_2)}{a_2 - ans}}$

也就是 $t ×frac{a_2 - ans}{gcd(M,b_2)}$

那么 对于下一次 求解来说 

更新答案 : $ans = ans + t × M$,然后 $ans = (ans~mod~M + M)~mod~M$ 保证$ans$ 是最小的正整数解

  然后因为 $$LCM(M,b2) =  frac{M×b_2}{gcd(M,b_2)}$$ 所以更新$M$ $$M *= frac{b_2}{gcd(M,b_2)}$$。

同理 循环 $n - 1$ 次 得到最小的 $ans$ 也就是要求的 $x$

下面贴代码

#include <cstdio>
#include <cctype>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

const int maxn = 100005;

int n;


LL ai[maxn],bi[maxn];

LL quickmul(LL a,LL b,LL mod)// 这是龟速乘 
{
    LL res = 0;
    
    while(b)
    {
        if(b & 1) res = (res + a) % mod;
        
        a = (a + a) % mod;
        b >>= 1; 
    }
    
    return res % mod;
}

LL exGcd(LL a,LL b,LL &x,LL &y)// 扩欧 
{
    if(!b)
    {
        x = 1;
        y = 0;
        return a;
    }
    
    LL r = exGcd(b,a%b,x,y);
    
    LL t = x; x = y; y = t - a/b * y;
    
    return r;
}

LL exCrt()
{
    LL x,y;
    
    LL M = bi[1], ans = ai[1];//  特判 第一组 解 
    
    for(int i=2;i<=n;i++)
    {
        LL a = M, b = bi[i], c = (ai[i] - ans % b + b) % b;// a*x ≡ c (mod b) 
        
        LL gcd = exGcd(a,b,x,y);
        
        x = quickmul(x,c/gcd,b/gcd);// 相当于 x *= c/gcd 
        
        ans += x * M;
        
        M *= (b / gcd);
        
        ans = (ans % M + M) % M;// 找最小的ans 
    }
    
    return (ans % M + M) % M;
}

int main()
{
    scanf("%d",&n);
    
    for(int i=1;i<=n;i++)
        scanf("%d%d",&bi[i],&ai[i]);
        
    printf("%lld",exCrt());
    
    return 0;
}

 

原文地址:https://www.cnblogs.com/-Wind-/p/10705070.html