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

前言

与 这个 定理 搏斗了 一天终于 把它kill 了 哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈

进入正题


题目描述

给定 n 组 非负整数 ai,bi,求解关于 x 的方程组

            x ≡ a1 (mod b1

            x ≡ a2 (mod b2)

            .

            .

            x ≡ an (mod bn)

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

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

从第二组开始:

        LCM(b1,b2) =  b1×b2 /(gcd(b1,b2))这个不需要证明

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

                 LCM(M,b2) =  M×b2 /(gcd(M,b2))

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

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

    ans + t×M ≡ a2 (mod b2) 此时 ans 是a1 是已知的

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

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

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

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

也就是说 我们解得的t 其实是 t × M ≡ gcd(M,b2) (mod b2) 的解

所以 a2 - ans 到 gcd(M,b2) 相当于是乘了一个 gcd(M,b2)/(a2 - ans) 所以我们得到的解 也相当于是 乘了 一个gcd(M,b2)/(a2 - ans)

那么真正的 t 应该是 t/(gcd(M,b2)/(a2 - ans))

也就是 t * (a2 - ans)/gcd(M,b2)

那么 对于下一次 求解来说 

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

      因为 LCM(M,b2) =  M×b2 /(gcd(M,b2)) 所以 M *= b2 /(gcd(M,b2))。

同理 循环 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;
}

 

猜你喜欢

转载自www.cnblogs.com/-Wind-/p/10705070.html
今日推荐