• 中国剩余定理及扩展中国剩余定理瞎讲(CRT&EXCRT)


    中国剩余定理(CRT)

    怎么说

    我听了三遍才懂emmm

    再次感谢wjh dalao倾情讲解加手写演绎

    这样一个同余方程组,要求一个x

    使x满足以上所有条件,其中m1——mk互素

    将m1到mk累乘

    构造M = m1 * m2 * ... * mk

    因为m互素,所以M为m的lcm

    这样易得

    M / mi ≡ 0 (mod mj) (i != j)

    利用exgcd求出M / mi的逆元,使得

    M / mi * ti ≡ 1 (mod mi) 

    (不会exgcd指路这里——>同余方程,exgcd

    (不会逆元指路这里——>乘法逆元

    我们知道在mod m意义下,两边同乘不干扰

    所以将上式两边同乘ai

    ai * M / mi * ti ≡ ai (mod mi)

    我们发现,这个式子的右边是原方程组的右边

    那么左边就自然是x的值了

    而x需要满足所有的同余方程

    那么

    特别的 (x % M + M) % M是最小整数解

    两道模版题

    luoguP3868猜数字

    P1495曹冲养猪

    放一段crt的核心代码

    ll quickmul(ll n,ll k){
        ll ans = 0;
        while(k > 0){
            if(k & 1)
            ans = (ans + n) % M;
            n = (n + n) % M;
            k >>= 1;
        }
        return ans % M;
    }//猜数字中需要用到快速乘
    
    void exgcd(ll a,ll b,ll &x,ll &y){
        if(b == 0){
            x = 1;
            y = 0;
            return ;
        }
        exgcd(b,a % b,x,y);
        ll tmp = x;
        x = y;
        y = tmp - a / b * y;
    }//exgcd
    
    int main() {
        int k;
        M = 1;
        scanf("%d",&k);
        for(int i = 1; i <= k; i++)
            scanf("%lld",&a[i]);
        for(int i = 1; i <= k; i++) {
            scanf("%lld",&m[i]);
            M *= m[i];
        }
        ll ans = 0;
        for(int i = 1; i <= k; i++)
            a[i] = (a[i] % m[i] + m[i]) % m[I];//先将a处理一下
        for(int i = 1; i <= k; i++){
            ll x,y;
            exgcd(M / m[i],m[i],x,y);//求出ti,即为x
            x = (x % m[i] + m[i]) % m[i];
            ans = (ans + quickmul(quickmul(M / m[i],x),a[i])) % M; //多膜
        }
        printf("%lld",(ans % M + M) % M);//多膜
        return 0;
    }

    扩展中国剩余定理(EXCRT)

    看了一篇博客就看懂了

    但是做题的话

    想起来会比较难

    写一下博客加深印象emmm

    首先还是一个同余方程组

    不过这次m1——mk啥关系没有【滑稽

    我们从只有两组方程开始考虑

    可以得到:

    ak∗ m1

    ak∗ m2

    所以 

    ak∗ mak∗ m2

    k2m2k1m1=a1a2

    有没有一点眼熟?

    是不是很像

    a * x + b * y = c

    这样我们设

    c = a1 - a2

    这样我们需要c是gcd(m1,m2)的倍数才能求出解

    如果不是倍数无解

    如果是的话,就可以用exgcd求出 k2 * m2 + k1 * m1 = gcd(m1,m2)中的k1的值

    c为gcd(m1,m2)的倍数

    则 k1 = k1 * c / gcd(m1,m2)

    题解说最好 k1 % 一下 m2怕爆long long

    这样我们可以反推出来x

    x = a1 - k1 * m1

    可以得到通解是:x k * lcm(m1,m2)

    将这个方程转化一下,可以得到新的同余方程

    x(molcm(m1,m2)

    于是我们把两个方程变成了一个

    以此类推

    最终可以得到结果

    luoguP4777模版 扩展中国剩余定理

    这是一道版子题

    但是它保证数据都有解

    看一下代码

    #include<cstdio>
    #define sev en
    using namespace std;
    #define ll long long
    #define N 100010
    
    ll a[N],m[N],M;
    ll ans;
    
    ll quickmul(ll a,ll k,ll z){
        ll  res = 0;
        while(k){
            if(k & 1)
            res = (res + a) % z;
            a = (a + a) % z;
            k >>= 1;
        }
        return res;
    }//快速乘了一下
    
    ll exgcd(ll a,ll b,ll &x,ll &y){
        if(b == 0){
            x = 1;
            y = 0;
            return a;
        }
        ll as = exgcd(b,a % b,x,y);
        ll tmp = x;
        x = y;
        y = tmp - a / b * y;
        return as;
    }//exgcd
    
    int main(){
        int n;
        scanf("%d",&n);
        for(int i = 1;i <= n;i++)
        scanf("%lld%lld",&m[i],&a[i]);//这里的输入我给反过来了
        ans = a[1],M = m[1];//首先把第一组同余方程的值赋上
        for(int i = 2;i <= n;i++){
            ll x,y;
            ll s = (a[i] - ans % m[i] + m[i]) % m[i];//这里相当于a1 - a2 = c
            ll gcd = exgcd(M,m[i],x,y);//求gcd
            x = quickmul(x,s / gcd,m[i]);//x为k1,k1 * c / gcd
            ans += x * M;//ans加上求出的值
            M *= m[i] / gcd;//把两个同余方程的模数合并
            ans = (ans % M + M) % M;//最小整数解
         }
        printf("%lld",(ans % M + M) % M);
        return 0;
    }

    需要注意的是,如果不保证数据一定有解

    就把excrt的部分写在外面

    返回ans再输出

    中途判断 s 是否是 gcd 的倍数

    不是的话直接返回-1或看题目要求

    然后就没有然后啦

    写完了感觉还不错emmmm(自我欣赏一下www

    理解的海星,jio的最近效率蛮高的2333
     

  • 相关阅读:
    HTML技巧: 语义化你的代码
    css sprite
    Redis主从复制原理
    idea修改快捷键
    Ubuntu14.20 安装docker,创建centos6.7容器,并访问centos容器
    [转]SQL 中 with as 的用法
    ftp与sftp及sftp和scp的区别
    Linux top 命令
    Linux free 命令
    ubuntu 源方式 安装jdk
  • 原文地址:https://www.cnblogs.com/sevenyuanluo/p/10539609.html
Copyright © 2020-2023  润新知