zoukankan      html  css  js  c++  java
  • CRT&&EXCRT学习笔记

    这是个大坑,今天填了,本来应该好久以前就会的(但硬是没学)


    关于CRT

    CRT就是中国剩余定理,是一个没什么泛用性但是偶尔会考的东西

    (相比之下没怎么考过的欧拉函数就显得比较惨)

    其中mi两两互质,求最小的x

    首先构造M为所有mi的乘积,然后Mi为M/mi,易得Mi和mi互质,

    因此必有bi使Mi*bi = 1(mod mi) 即bi为模mi意义下Mi的逆元,

    所以可以得到Mi*bi*ai = ai(mod mi)

    把所有Mi*bi*ai相加就是一个合法解x(这是一个没有证的东西)

    为了使x为最小正整数,所以x还要模mi的LCT(不是指LINK-CUT-TREE)

    即x = (x%M+M)%M(因为mi两两互质所以LCT就是M)

    愉快。


    Luogu P3868 猜数字

    裸体板子,式子移个项就是CRT

    但是出题人很鹅心,要用龟速乘处理一个爆longlong的点,还有就是要时刻注意逆元的正负和大小

    对于一个最小正整数逆元可以x[i] = (x[i]%m[i] + m[i]) % m[i](相当于调整EXGCD中x和y)

    丢人蒟蒻把乘的式子写错了(快滚快滚)

    #include<bits/stdc++.h>
    
    using namespace std ;
    
    const int MAXN = 20;
    long long K,a[MAXN],Mi[MAXN],M,m[MAXN],ans,x[MAXN];
    
    void exgcd(long long a,long long b,long long &x,long long &y){
        if(b==0){
            x=1,y=0;
            return;
        }
        exgcd(b,a%b,x,y);
        long long xx=x,yy=y;
        x=yy;
        y=xx-(a/b)*yy;
        return ;
    }
    
    long long inv(long long a,long long mod){
        long long xxx=0,y=0;
        exgcd(a,mod,xxx,y);
        return xxx;
    }
    
    long long qm(long long a,long long b,long long mod){
        long long ret=0;
        while(b){
            if(b&1) (ret+=a)%=mod;
            a=(a+a)%mod;
            b>>=1;
        }
        return ret;
    }
    
    int main(){
        ios::sync_with_stdio(false);
        cin>>K;
        for(int i=1;i<=K;i++){
            cin>>a[i];
        }
        for(int i=1;i<=K;i++){
            cin>>m[i];
        }
        M=1;
        for(int i=1;i<=K;i++){
            M *= m[i];
        }
        for(int i=1;i<=K;i++){
            Mi[i] = M / m[i];
        }
        for(int i=1;i<=K;i++){
            x[i] = inv(Mi[i],m[i]);
    //        cout<<"Mi[i] = "<<Mi[i]<<"m[i] = "<<m[i]<<endl;
    //        cout<<"x[i] = "<<x[i]<<endl;
            x[i] = (x[i]%m[i] + m[i]) % m[i];
        }
        for(int i=1;i<=K;i++){
            ans += qm(qm(a[i],Mi[i],M),x[i],M);
            ans %= M;
        }
        
    //    cout<<"M = "<<M<<endl;
    //    for(int i=1;i<=K;i++){
    //        cout<<"m"<<i<<" = "<<m[i]<<"M"<<i<<" = "<<Mi[i]<<" "<<endl;
    //    }
    //    for(int i=1;i<=K;i++){
    //        cout<<"x"<<i<<" = "<<x[i]<<" "<<endl;
    //    }
        cout<<ans<<endl;
        return 0;
    }
    //凝聚SB气息的调试信息...

    这个是CRT,只适用于m两两互质的情况,如果m没有任何特殊条件呢?

    这就是EXCRT了


    关于EXCRT

    把EXCRT要干的的事情用式子写出来就是

    其中mi不一定两两互质,求最小的正整数解x

    网上看了几个版本,有一种合并的求x方式,同机房几个大佬都写过了,这里不再赘述

    但有一种容易理解的写法(niiick的博客),感觉非常好

    大概是这样的:

    对于第一个式子解为a[1]%m[1](是啊仔细想想)

    如果对于前k-1个式子已经有了一个最小非负整数解x,并且设M为前k-1个m的乘积,

    那么对于前k-1个式子来说,所有解x的表达式为 {x(all)} = x + O * M (O为整数)

    现在要用这个表达式,找一个解x,使对于前k个式子来说,x = a[i] (mod m[i]) (1<=i<=k)

    (我这种暴力选手看到这里直接想向上枚举了,但是经验告诉我们不能乱搞)

    可以把对于前k-1个式子都合法的那个x叫做x0(没有Markdown原谅下⑧)

    那么我们要求x0 + t * M = a[k] (mod m[k])

        变形为t * M + O * m[k] = a[k] - x0 (O为任意整数系数)

    首先判断a[k]-x0是不是gcd(M,m[k])的倍数,不是则整个方程式无解

    是则EXGCD求解t,

    然后处理t使其最小正整数(与m[k]交易),

    然后x += t * M,处理M,接着处理下一个式子

    最后全部式子处理完之后得到答案x

    也比较愉快。去看题


    Luogu P4777 【模板】扩展中国剩余定理(EXCRT)

    数据范围比CRT大多了,同时观察到a和b的给出方式利于在线处理

    还有一堆小细节要处理,要乘什么,式子表示的是不是对的

    当前系数表示的东西是什么,怎么维护才是正确的

    这一步要不要取模,balabala,还有,HAVE FUN DEBUGING

     

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int MAXN = 100010;
    long long n,ai[MAXN],bi[MAXN];
    
    long long qm(long long a,long long b,long long mod){
        long long ret=0;
        while (b){
            if(b&1) (ret += a) %= mod;
            a = (a+a) % mod;
            b>>=1;
        }
        return ret;
    }
    
    long long  exgcd(long long  a,long long  b,long long  &x,long long  &y) { 
        if(b==0){x=1;y=0;return a;} 
        long long  gcd=exgcd(b,a%b,x,y); 
        long long  tp=x; x=y; y=tp-a/b*y; 
        return gcd;
    } 
    
    long long  excrt() { 
         long long x,y,k;
          long long M=bi[1],ans=ai[1];
           for(int i=2;i<=n;i++) { 
               long long  a=M,b=bi[i],c=(ai[i]-ans%b+b)%b;
            long long  gcd=exgcd(a,b,x,y),bg=b/gcd;
    //         if(c%gcd!=0) return -1;
              x=qm(x,c/gcd,bg); 
              ans+=x*M; 
              M*=bg; 
              ans=(ans%M+M)%M;
        } 
        return (ans%M+M)%M; 
    }
    
    signed main(){
        ios::sync_with_stdio(false);
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>bi[i]>>ai[i];
        }
        cout<<excrt()<<endl;
        return 0;
    }

     TAG:SIN_XIII ⑨

     

  • 相关阅读:
    SeaweedFS上手使用指南
    XyTalk企业即时通讯IM开始开源
    大数据项目相关技术栈(Hadoop周边技术)
    Hive SQL基础操作
    Applet Mode
    快速开始
    NetBeans启动Tomcat报“'127.0.0.1' 不是内部或外部命令”启动失败的解决办法
    运行带distance field的Hiero
    Game Loop的几种实现方式
    20150408
  • 原文地址:https://www.cnblogs.com/SINXIII/p/11233857.html
Copyright © 2011-2022 走看看