zoukankan      html  css  js  c++  java
  • [笔记] CRT & exCRT

    [笔记] CRT & exCRT

    构造法

    求多组(x equiv r_i (mod d_i))的解,(d_i)互质

    余数((r_i = remainder)),除数((d_i=divisor))

    我们想啊,如果我们能找到一个数 (k1equiv1(mod ext{ }3))(5)(7) 的倍数

    一个数 $k2equiv1(mod ext{ }5) $是(3)(7)的倍数

    一个数 $k3equiv1(mod ext{ }7) $是(3)(5)的倍数

    那么这样的话我们的答案就是 (k1*2+k2*3+k3*2)

    这样的话就是如何求这三个数了

    首先我们求出 (3,5,7) 的$ lcm=105$

    之后令

    (x1=105/3=35,x2=105/5=21,x3=105/7=15)

    我们可以得到三个线性同余方程

    (35aequiv1(mod ext{ }3))

    (21bequiv1(mod ext{ }5))

    (15cequiv1(mod ext{ }7))

    直接上扩欧就行了

    所以$ k1=35a=35*2=70$

    (k2=21b=21∗1=21)

    (k3=15c=15∗1=15)

    之后我们的答案就是

    ((k1∗2+k2∗3+k3∗2)mod lcm=23)

    曹冲养猪

    板子from wzx(我的构造法过不了,不过有了扩欧也不想用这个方法了)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define LL long long
    #define re register
    #define maxn 25
    using namespace std;
    inline LL exgcd(LL a,LL b,LL &x,LL &y)
    {
        if(!b)
        {
            x=1,y=0;
            return a;
        }
        LL r=exgcd(b,a%b,y,x);
        y-=a/b*x;
        return r;
    }
    inline LL read()
    {
        char c=getchar();
        LL x=0;
        while(c<'0'||c>'9') c=getchar();
        while(c>='0'&&c<='9')
          x=(x<<3)+(x<<1)+c-48,c=getchar();
        return x;
    }
    LL a[maxn],b[maxn];
    LL x,y;
    int n;
    LL lcm=1;
    int main()
    {
        n=read();
        for(re int i=1;i<=n;i++)
        {
            b[i]=read();
            a[i]=read();
            lcm*=b[i];
        }
        LL ans=0;
        for(re int i=1;i<=n;i++)
        {
            LL xx=lcm/b[i];
            LL r=exgcd(xx,b[i],x,y);
            x=(x%b[i]+b[i])%b[i];
            ans=(ans+x*xx*a[i]%lcm)%lcm;
        }
        cout<<ans<<endl;
        return 0;
    }
    

    扩欧法

    [egin{cases} x &equiv& r_1 pmod{d_1}\ x & equiv& r_2 pmod{d_2}\ &vdots\ x & equiv& r_n pmod{d_n} end{cases}]

    此时如果(d_1,d_2,dots,d_n)不互质怎么办?

    观察两个同余式

    • $xequiv r_1 pmod{d_1} $
    • (xequiv r_2 pmod{d_2})

    可以化为

    • (x=k_1d_1 + r_1)
    • (x=k_2d_2+r_2)

    因此

    • (k_2d_2+r_2=k_1d_1+r_1)
    • (k_2d_2-k_1d_1=r_1-r_2)

    是不是有点像(ax+bx=c)

    于是我们可以解出来

    • (k_2 ^{'} d_2+k_1 ^{'} d_1=gcd(d_1,d_2))

    并乘上((r_1-r_2)/gcd(d_1,d_2))得到之前式子的解

    • (k_1=k_1 ^{'} cdot (r_1-r_2)/gcd(d_1,d_2))

    但是得(gcd(d_1,d_2)|(r_1-r_2)),不然是得不到通解的

    因为是带着一个负号的,所以得到

    • (x_0= -k_1d_1+r_1)

    得到新方程(xequiv x_0 pmod{ lcm(d_1,d_2)})

    好的,开始,抠细节

    • 首先,最后要取模(lcm(d1,d2)),快速乘的时候忘记了,挂掉x1
    • 快速乘里面不能有负数,挂掉x2
    • 快速乘也要取模(lcm(d_1,d_2)),挂掉x3
    • (lcm(d_1,d_2))应该这么写,(d_1 /gcd(d_1,d_2)cdot d_2),而不是(d_1 cdot d_2 / gcd(d_1,d_2)),挂掉x4
    • 把除号放里面,不然有逆元,(其实是因为模了一个不该模的东西)挂掉x5

    代码

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long ll;
    
    const int N = 1e5 + 10;
    
    ll n, x, y;
    ll d[N], r[N];
    
    ll mul(ll a, ll b, ll p){//龟速乘
        int f = 1;
        if(a < 0) f = -f, a = -a;
        if(b < 0) f = -f, b = -b;
        ll w = 0;
        while(b){
            if(b & 1)
                w = (w + a) % p;
            b >>= 1;
            a = (a + a) % p;
        }
        return w * f;
    }
    
    ll exgcd(ll a, ll b){//扩欧
        ll ans, t;
        if(b == 0){
            x = 1;
            y = 0;
            return a;
        } else {
            ans = exgcd(b, a % b);
            t = x;
            x = y;
            y = t - a / b * y;
        }
            
        return ans;
    }
    
    ll exCRT(){
        
        for(int i = 2; i <= n; ++i){
            ll C = r[1] - r[i];
            ll D = exgcd(d[i], d[1]);
            if(C % D) return -1;
            ll k1 = mul(y , c / D, d[1] / D * d[i] );
            ll x0 = mul(-k1 , d[1], d[1] / D * d[i] ) + r[1];
            d[1] = d[1] / D * d[i], r[1] = x0;
            r[1] = (r[1] % d[1] + d[1]) % d[1];//先模一遍,让绝对值小于,然后处理
        }
        return r[1];
    }
    
    int main(){
        scanf("%lld", &n);
        for(int i = 1; i <= n; ++i){
            scanf("%lld %lld", &d[i], &r[i]);
        }
        long long ans = exCRT();
        printf("%lld", ans);
        return 0;
    } 
    

    我知道写的超级丑,比不上fym

  • 相关阅读:
    操作mysql的指令
    linux下安装mongodb
    linux将指令加入开机启动或加入环境变量
    php请求远程url内容方法
    nosql基本了解
    堆排序的实现
    转:导出csv文件数字会自动变科学计数法的解决方法
    jQuery筛选结果等于true的筛选
    转:Jquery的parent和parents(找到某一特定的祖先元素)
    drupal7 带表达式条件的update
  • 原文地址:https://www.cnblogs.com/LMSH7/p/9534755.html
Copyright © 2011-2022 走看看