中国剩余定理与扩展中国剩余定理

泪湿孤枕 提交于 2020-01-10 03:29:26

中国剩余定理与扩展中国剩余定理

概述

中国剩余定理是同余中常用的辅助手段,常用来合并多个同余方程的解(如MTT)。其技术难度不高,代码也很短,可以在NOIP中出现。

中国剩余定理CRT

形式

给定 \(n\) 组非负整数 \(m_i, a_i\) ,求解关于 \(x\) 的方程组的最小非负整数解。
\[\begin{cases} x \equiv a_1\ ({\rm mod}\ m_1) \\ x\equiv a_2\ ({\rm mod}\ m_2) \\ ... \\ x \equiv a_n\ ({\rm mod}\ m_n)\end{cases}\]

其中\(m_i\)两两互质。

解法

我们贪心构造和式使得每一项在模意义下不影响其他项,并且符合当前的方程,
\(M =\prod^{n}_{i=1}m_i,M_i=\frac{M}{m_i}\),可以构造解
\[x=\sum^{n}_{i=1}M_i(a_iM_i^{-1} \mod m_i)\]
可以证明解在模M 意义下唯一。

模板题:P3868 [TJOI2009]猜数字
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF=1e9+7,MAXN=11;
inline LL mult(LL a,LL b,LL mod){
    LL ret=0;
    while(b>0){
        if(b&1)
            ret=(ret+a)%mod;
        a=(a+a)%mod;
        b>>=1;
    }
    return ret;
}
void exgcd(LL a,LL b,LL &x,LL &y){
    if(!b){
        x=1;
        y=0;
        return;
    }
    exgcd(b,a%b,y,x);
    y-=a/b*x;
}
inline LL inv(LL x,LL p){
    LL a,b;
    exgcd(x,p,a,b);
    return (a%p+p)%p;
}
int N;
LL a[MAXN],b[MAXN],M,lcm=1,ans;
int main(){
    scanf("%d",&N);
    for(int i=1;i<=N;i++)
        scanf("%lld",a+i);
    for(int i=1;i<=N;i++){
        scanf("%lld",b+i);
        lcm*=b[i];
        a[i]=(a[i]%b[i]+b[i])%b[i];
    }
    for(int i=1;i<=N;i++){
        LL Mi=lcm/b[i],t=inv(Mi,b[i]);
        ans=(ans+mult(mult(Mi,t,lcm),a[i],lcm))%lcm;
    }
    printf("%lld",(ans+lcm)%lcm);
    return 0;
}

扩展中国剩余定理EXCRT

形式

给定 \(n\) 组非负整数 \(m_i, a_i\) ,求解关于 \(x\) 的方程组的最小非负整数解。
\[\begin{cases} x \equiv a_1\ ({\rm mod}\ m_1) \\ x\equiv a_2\ ({\rm mod}\ m_2) \\ ... \\ x \equiv a_n\ ({\rm mod}\ m_n)\end{cases}\]

解法

扩展中国剩余定理的方法和中国剩余定理关系不大,我们贪心处理前\(i-1\)项,然后合并当前方程。
将每个方程拆成若干个方程
\[x=a_i(\mod p_{i,j}^{k_{i,j}})\]
其中\(m_i=\prod p_{i,j}^{k_{i,j}}\)\(m_i\) 的分解式。对每个质数\(p\),合并对应的所有方程,从而转化为模数两两互质的情形。若合并过程中出现矛盾,则原方程组无解。

模板题:P4777 【模板】扩展中国剩余定理(EXCRT)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF=1e9+7,MAXN=1e5+10;
inline LL mult(LL a,LL b,LL mod){
    LL ret=0;
    while(b>0){
        if(b&1)
            ret=(ret+a)%mod;
        a=(a+a)%mod;
        b>>=1;
    }
    return ret;
}
LL exgcd(LL a,LL b,LL &x,LL &y){
    if(!b){
        x=1;
        y=0;
        return a;
    }
    LL ret=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return ret;
}
int N;
LL a[MAXN],b[MAXN],M,ans,x,y;
int main(){
    scanf("%d",&N);
    for(int i=1;i<=N;i++){
        scanf("%lld%lld",b+i,a+i);
        a[i]=(a[i]%b[i]+b[i])%b[i];
    }
    M=b[1];
    ans=a[1];
    for(int i=2;i<=N;i++){
        LL rm=(a[i]-ans%b[i]+b[i])%b[i];
        LL gcd=exgcd(M,b[i],x,y);
        LL bg=b[i]/gcd; 
        /*if(rm%gcd):ERROR*/
        x=mult(x,rm/gcd,bg);
        ans+=x*M;
        M*=bg;
        ans=(ans%M+M)%M;
    }
    printf("%lld",ans);
    return 0;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!