Loading

CRT

CRT 极简记录

CRT

CRT 原名为中国剩余定理,即 China Remainder Theorem。能够解决同于方程组的问题。

问题原型

解同余方程组:

{xa1modm1xa2modm2xa3modm3...xanmodmn

其中满足所有 mi 互质。

M=mi,Mi=Mmi,tiMi1modmi

则最小解 x=i=1nMitiaimodM

但注意普通的 CRT 必须满足 mi 互质,否则不成立,因为不互质不一定能够构造出来 tiMi1modmi

证明

证明是好理解的,首先,j[1,n],jiMjtjaj0modmi,因为 Mi 中有因子 mi。之后又有 Mitiaiaimodmi,因为 tiMi1modmi

这样加起来之后的数,一定能够保证其正确性。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=2e5+10;
void exgcd(ll a,ll b,ll &x,ll &y){
    if(b==0){x=1,y=0;}
    else exgcd(b,a%b,y,x),y-=a/b*x;
}
ll inv(ll x,ll mod){
    ll q,p;
    exgcd(x,mod,q,p);
    return (q%mod+mod)%mod;
}
int n,a[maxn],m[maxn];
ll M=1;
int main(){
    cin.tie(0);
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++)cin>>m[i]>>a[i],M*=1ll*m[i];
    ll ans=0;
    for(int i=1;i<=n;i++){
        ll Mi=M/(1ll*m[i]);
        ll t=inv(Mi,1ll*m[i]);
        ans=(ans+Mi*t*1ll*a[i])%M;
    }
    cout<<ans<<"\n";
    return 0;
}

EXCRT

那么有没有一个真正的同解,即没有 mi 限制的条件的解法呢?肯定有的,但是其逻辑和原来的 CRT 是完全不一样的,因为 CRT 其实只是简单地构造出来了一些等于 0 和等于 1,的解,但是想要再进一步很困难,我们尝试回到两个同余方程组的本质。

{xa1modm1xa2modm2

尝试将其合并,可以得到a1+k1m1=a2+k2m2,之后移项有 k1m1k2m2=a2a1。对于这个方程,可以简单用裴蜀定理来判断是否有解,即是否满足 gcd(m1,m2)|a2a1

接着考虑如何解一个解出来,这里设 gcd(m1,m2)=d 则将两侧同时除以 dk1m1dk1m2d=a2a1d

左边两个系数是互质的,因此我们解出来 Xm1d+Ym2d=1 之后可以得到

{k1=Xa2a1dk2=Ya2a1d

则有 x=a1+m1Xa2a1d

这样,将两个方程合并之后,我们有

xa1+m1Xa2a1dmodlcm(m1,m2)

至于证明唯一性,可以简单的设在 lcm(m1,m2) 范围内有两个解,发现矛盾。

之后通过不断合并两个方程,我们就可以得到最后的解。

#include<bits/stdc++.h>
using namespace std;
#define ll __int128
inline ll read(){
    ll res=0;
    char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch)){
        res=(res<<1)+(res<<3)+(ch-'0');
        ch=getchar();
    }
    return res;
}
inline void pr(ll x){
    if(x>=10)pr(x/10);
    putchar(x%10+'0');
}
const int maxn=2e5+10;
int n;
ll a[maxn],b[maxn];
ll gcd(ll x,ll y){
    if(y==0)return x;
    return gcd(y,x%y);
}
void exgcd(ll a,ll b,ll &x,ll &y){
    if(!b){x=1,y=0;}
    else exgcd(b,a%b,y,x),y-=a/b*x;
}
ll lcm(ll x,ll y){
    return x/gcd(x,y)*y;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)a[i]=read(),b[i]=read();
    ll A=a[1],B=b[1];
    for(int i=2;i<=n;i++){
        ll d=gcd(A,a[i]),X=0,Y=0;
        exgcd(A/d,a[i]/d,X,Y);
        if(X<0)X+=a[i]/d;
        else X%=a[i]/d;
        ll LCM=lcm(A,a[i]);
        B+=(b[i]-B)/d%LCM*A%LCM*X%LCM,A=LCM;
        if(B<0)B+=A;
    }
    pr(B%A);
    return 0;
}
posted @   Jryno1  阅读(27)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示