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

非扩展

用于求解线性同余方程组 ,其中模数两两互质 .

先来看一看两个显然的定理:

1.若 x \(\equiv\) 0 (mod p) 且 y \(\equiv\) 0 (mod p) ,则有 x+y \(\equiv\) 0 (mod p)

2.若 x \(\equiv\) b (mod p) 且 y \(\equiv\) 0 (mod p), 则有 x+y \(\equiv\) b (mod p) (0$\leq $b<p)

则整个方程组可以写为

b1\(\begin{bmatrix}1\\0\\0\end{bmatrix}\) + $\cdots $+ bi\(\begin{bmatrix}0\\1\\0\end{bmatrix}\) + $\cdots $ + bn\(\begin{bmatrix}0\\0\\1\end{bmatrix}\)

也就是说,想要求出最终方程组的解就只需要求出上面每个行列式的值。

考虑每个式子中,因为每两个模数之间两两互质,设 N=$ \sum_{i=1}^nm_i$

则GCD(\(\frac{N}{m_i}\) , mi) = 1 , N/mi即为其他模数的lcm,即为其他n-1个方程的解,第i个方程的解可以写成xi=N / mi * y

即 N/mi * y \(\equiv\) 1 (mod mi)

此方程等价于 N/mi * y + mi * z=1 ,可以用扩展欧几里得求解。

求出xi后 ,有 bi * xi $\equiv $ bi (mod mi)

最后整个方程组的解就为 X = $\sum_{i=1}^n b_i * x_i $ mod N

Code:


#include<stdio.h>
#define N 100

long long W[N],B[N],nn,T;
inline void exgcd(long long a,long long b,long long &x,long long &y){
    if(!b){
        x=1;y=0;
    }else{
        exgcd(b,a%b,x,y);
        long long t=x;
        x=y;
        y=t-a/b*y;
    }
}
inline long long China(long long k){
    long long x,y,a=0,m,n=1;
    for(long long i=0;i<k;i++) n*=W[i];
    for(long long i=0;i<k;i++){
        m=n/W[i];
        exgcd(W[i],m,x,y);
        a=a+y*B[i]*m;
    }
    a%=n;
    while(a<=0) a+=n;
    while(a>=n) a-=n;
    return a;
}
signed main(){
    scanf("%lld",&nn);
    for(long long i=0;i<nn;i++)
        scanf("%lld",&B[i]);
    for(long long i=0;i<nn;i++)
        scanf("%lld",&W[i]);
    for(long long i=0;i<nn;i++)
        B[i]=(B[i]%W[i]+W[i])%W[i];
    printf("%lld",China(nn));
}

扩展

刚刚学习了中国剩余定理,现在来学习它的扩展。先说句闲话,不知道是不是扩展过头了,其实中国剩余定理和它的扩展关系不大,所以先请忘记刚刚在上文所看到的一切套路,开始新的征程。

好了,现在模数不是两两互质的了,也就是说先前求的N不是它们的LCM,不能直接用。

我们考虑构造的方法,假设我们已经求出了前k-1个方程的和解,现在需要合并第k个方程。

设前k-1个方程的和解为X,则他们的通解可以写成 X + t * N (其中N为前k-1个模数的LCM)

则要合并第k个方程,即要解如下方程
\[ X + t * N \equiv b_k ( mod m_k ) \]

此方程又等价于 \[ t * N + m_k * y = b_k-X \]

晃眼一看,又是扩展欧几里得。求出 t 后 ,前k个方程的解就为 X' = X + t * N , 继续更新 N ,N = LCM(N,\(m_k\))

Code:

#include<stdio.h>
#define N 100006
#define ll long long

ll a[N],b[N],ans,B,M,GCD,x,y;
int n;
template<class T>
inline void read(T &x){
    x=0;T flag=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') flag=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
    x*=flag;
}
inline ll exgcd(ll a,ll b,ll &xx,ll &yy){
    if(!b){
        xx=1;yy=0;
        return a;
    }
    ll gcd=exgcd(b,a%b,xx,yy);
    ll tmp=xx;
    xx=yy;
    yy=tmp-(a/b)*yy;
    return gcd;
}
inline ll mul(ll a,ll b,ll Mod){
    ll ans=0;
    while(b){
        if(b&1) ans=(ans+a)%Mod;
        a=(a<<1)%Mod;
        b>>=1;
    }
    return ans;
}
int main(){
    read(n);
    for(int i=1;i<=n;i++)
        read(a[i]),read(b[i]);
    ans=b[1];
    M=a[1];
    for(int i=2;i<=n;i++){
        B=(b[i]-ans%a[i]+a[i])%a[i];
        GCD=exgcd(M,a[i],x,y);
        x=mul(x,B/GCD,a[i]);
        ans+=M*x;
        M*=a[i]/GCD;
        ans=(ans%M+M)%M;
    }
    printf("%lld",ans);
}
/*
3
11 6
25 9
33 17
*/

转载于:https://www.cnblogs.com/wwlwQWQ/p/10545799.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值