数论 中国剩余定理

中国剩余定理(Chinese Remainder Theorem)

定义

又名孙子定理,最早出现在《孙子算经》中

对于下面的一元线性同余方程组:

(1){xa1(modm1)xa2(modm2)     xan(modmn)

中国剩余定理给出了其有解的判定条件及解集

判定条件: m1,m2,,mn 两两互质

通解:

  1. M=i=1nmi,Mi=Mmi

  2. tiMimi 的逆元

  3. 方程组通解的形式就为:

    x=a1t1M1+a2t2M2++antnMn(modM)=i=1naitiMi(modM)

证明

证明: x=i=1naitiMi 是方程组的一个解

 i,j{1,2,,n},ij, gcd(mi,mj)=1tiMi1(modmi) x=a1t1M1+a2t2M2++antnMn x%mi=j=1n(ajtjMj%mi) j{1,2,,n},ji, mi|Mj x%mi=aitiMi%mi tiMi1(modmi) tiMi=1(modmi) x%mi=ai x

证明: x=kM+i=1naitiMi 是方程组的通解

 x1.x2 i{1,2,,n} x1%mi=ai,x2%mi=ai x1%mix2%mi=(x1x2)%mi      =aiai=0 mi | (x1x2) m1,m2,,mn  i=1nmi | (x1x2) M | (x1x2) M,

用法

O(nlogn)

typedef long long lld;
const int maxn=10+12;
lld a[maxn],m[maxn],M=1;
int n;
//exGcd
lld gcd(lld a,lld b,lld&x,lld&y){
    if(b==0){
        x=1;y=0;return a;
    }
    lld g=gcd(b,a%b,y,x);
    y=y-a/b*x;
    return g;
}

lld crt(lld a[],lld m[]){
    lld ans=0,t,y;//t refers to M/m[i]'s Inverse Element
    for(int i=1;i<=n;i++){
        gcd(M/m[i],m[i],t,y);//M refers to \prod_{i=1}^n(m[i])
        //M should be preprocessed
        //use exGcd to get Inverse Element
        //now t is M/m[i]'s Inserse Element
        ans=(ans+(M/m[i]*t*a[i])%M+M)%M;//According to the formula
        //Explian: why wo should use "(M/m[i]*t*a[i])%M+M"
        //that is to avoid negative numbers
        //we know that (x+M) is still the solution of the system of equations
        //when x is a negative number
        //x%M is a negative number between (-M,0)
        //so x%M+M is a positive number between (0,M)
        //in this way we could make sure that the answer is what we need
    }
    return ans;
}

ex-CRT

定义

扩展中国剩余定理,是对中国剩余定理的一个补充,用于解决中国剩余定理中模数 mi 不互质的情况。

中国剩余定理的限制

中国剩余定理对下面的这个方程组 S

(2)S={xa1(modm1)xa2(modm2)xan(modmn)

给出的通解形式为:

x=i=1naitiMi%M

其中 M=i=1nmi,MI=Mmi,Miti1(modmi)

不难发现当 Mimi 不互质时, ti 根本无法求得

所以这时需要扩展中国剩余定理 exCRT 出场了

(下文中均使用exCRT代指扩展中国剩余定理)

exCRT的基本思想

不同于中国剩余定理,exCRT尝试不停将两个方程合并为一个,这样直到方程只剩一个时,问题也就迎刃而解了

如何合并呢?

: xa1(modm1) xa2(modm2) x=a1+k1m1=a2+k2m2 k1m1k2m2=a2a1(k1,k2):(1) gcd(m1,m2)|(a2a1)k1,k2d=gcd(m1,m2),p1=m1d,p2=m2dk1p1k2p2=a2a1d d | (a2a1) a2a1dZ(λ1,λ2):λ1p1+λ2p2=1p1,p2  k1=a2a1dλ1,k2=a2a1dλ2 x=a1+a2a1dλ1m1 :(a1+a2a1dλ1m1)+k×lcm(m1,m2),kZ

为什么通解间一定间隔 lcm(m1,m2)

设上述方程组的解集中存在两个数 lcm(m1,m2)xy0N

有:

(3){xa1(modm1)xa2(modm2),{ya1(modm)1ya2(modm)2

(4){(xy)%m1=0(xy)%m2=0lcm(m1,m2) | (xy)

因为 x,ylcm(m1,m2)xylcm(m1,m2)

所以 xy=0x=y

得证

实现

typedef long long lld;
lld n,m[maxn],a[maxn];
inline lld times(lld a,lld b,lld mo){
    lld ans=0;
    while(b){
        if(b&1)an=(an+a)%mo;
        a=(a+a)%mo;
        b>>=1;
    }
    return an%mo;
}
inline lld gcd(lld a,lld b,lld &x,lld &y){
    if(b==0){
        x=1;y=0;return a;
    }
    lld g=gcd(b,a%b,y,x);
    y=y-a/b*x;
    return g;
}

inline void sol(){
    lld ans=a[1],M=m[1],x,y;
    for(int i=2;i<=n;i++){
        lld res=((a[i]-ans)%m[i]+m[i])%m[i];
        lld g=gcd(M,m[i],x,y);
        if(res%g!=0){
            printf("%d",-1);
            return;
        }
        x=times(x,res/g,m[i]);
        ans+=x*M;
        M=m[i]/g*M;
        ans=(ans%M+M)%M;
    }
    printf("%lld",ans);
}
posted @   Locked_Fog  阅读(189)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示
主题色彩