中国剩余定理与扩展模板

中国剩余定理:

解决模数互质的线性同余方程组

\left\{\begin{matrix} x & \equiv & 2 &(mod~3) \\ x & \equiv& 3 &(mod~5) \\ x& \equiv& 2& (mod~7) \end{matrix}\right.

中国剩余定理给出了这类方程的解法:

(1)先求 X1 使得 X1 整除5、7 并且 X1%3==2,再求 X2 使得 X2 整除3、7并且 X2%5==3,最后求 X3 使得 X3 整除3、5 并且 X3%7==2

(2)那么 X = X1 + X2 + X3 必然是上述同余方程组的一个解,验证第一个方程:X2%3==0,X3%3==0,X%3==X1%3==2(符合题意)

(3)求X1:X1整除5、7,设X1 = 35y,则有 35y = 2 (mod 3),由于gcd(35,3)==1;那么利用扩展欧几里得容易解出方程:35yy = 1 (mod 3) 的解yy,即 35*yy%3==1,但此方程的余数为1,不难发现35*yy*2%3==2,所以 X1= 35*yy*2;在模数互质的情况下,利用扩展欧几里得算法就可解得X1、X2、X3.....

(4)X + k*lcm(3,5,7) 必然为上述同余式的通解,那么最小正整数解为:X%lcm

模板:

///要求模数两两互质
void Ex_gcd(int a,int b,int &x,int &y) //扩欧
{
    if(b==0){x = 1;y = 0;return ;}
    Ex_gcd(b,a%b,x,y);
    int t = x;
    x = y;
    y = t - a/b*y;
}
int Crt()
{
    int a,b,x,y,res=0,M=1;
    for(int i=1;i<=n;++i) M = M*mod[i];
    for(int i=1;i<=n;++i)
    {
        int a = M/mod[i]; //相当于上文中的35
        int b = mod[i];
        Ex_gcd(a,b,x,y);
        x = (x%b+b)%b;   //yy的最小正整数解
        x = x*a*r[i];    //一组方程的解
        res = (x+res)%M;
    }
    return res == 0 ? M : res;
}

扩展中国剩余定理:

解决线性同余方程组(不讨论模数)

\left\{\begin{matrix} v & \equiv & a_1&(mod~b_1) \\ v & \equiv& a_2 &(mod~b_2) \\ v& \equiv& a_3& (mod~b_3) \end{matrix}\right.

 不难得出:

v=b_1x+a_1

v=b_2y+a_2

b_1x-b_2y=a_2-a_1

同样利用欧几里得算法求解上式的 x,那么前两个同余方程组的解 v1 = b1x + a1;通解为:v = k*lcm(b1,b2) + v1

又可以得到一个同余方程:

v\equiv v_1~(mod~lcm(b1,b2))

那么前两个方程合并成了一个方程,依次这样合并下去,得到最终解

模板:

int Ex_gcd(int a,int b,int &x,int &y)
{
    if(b==0){x=1;y=0;return a;}
    int d = Ex_gcd(b,a%b,x,y);
    int t = x;
    x = y;
    y = t - a/b*y;
    return d;
}
int Ex_Crt()
{
    int aa = b[1],rr=a[1],x,y;
    for(int i=2;i<=n;++i)
    {
        int bb = b[i];
        int c = a[i]-rr;
        int d = Ex_gcd(aa,bb,x,y);
        if(c%d) return -1;  //c不能整除d,不存在整数解
        c = c/d; bb = bb/d;
        x = (x*c%bb+bb)%bb;
        int lcm = aa*bb;
        rr = (x*aa%lcm+rr)%lcm;
        aa = lcm ;
    }
    return rr==0? aa:rr;
}

应用

(1)洛谷3868(模板+快速乘

///数据大了,先乘再取模,乘法就容易溢出,当然__int128可以避免
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 1e3+55;
ll aa[MAXN];
ll m[MAXN];
int n;
ll qmul(ll a,ll b,ll mod) //快速乘
{
    ll ans = 0;
    while(b>0)
    {
        if(b&1) ans=(ans+a)%mod;
        a = (a+a)%mod;
        b>>=1;
    }
    return ans;
}
void Ex_gcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0){x = 1;y = 0;return ;}
    Ex_gcd(b,a%b,x,y);
    ll tep = x;
    x = y;
    y = tep-a/b*y;
}
ll Crt()
{
    ll M = 1,res = 0;
    ll x,y;
    for(int i=1;i<=n;++i) M = M*m[i];
    for(int i=1;i<=n;++i)
    {
        ll tp = M/m[i];
        Ex_gcd(tp,m[i],x,y);
        x = (x%m[i]+m[i])%m[i];
        res = (res+qmul(qmul(tp,x,M),aa[i],M))%M;
    }
    return res==0? M:res;
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;++i) cin>>aa[i];
    for(int i=1;i<=n;++i) cin>>m[i];
    for(int i=1;i<=n;++i) aa[i]=(aa[i]%m[i]+m[i])%m[i];//余数不能比模数大
    cout<<Crt();
    return 0;
}



(2) 洛谷 4777(模板+_int128)

#include <cstdio>
#include <iostream>
typedef __int128 ll;
const int MAXN = 1e5+55;
ll m[MAXN],r[MAXN];
int n;
inline ll read()
{
    ll x=0,f=1;
    char c = getchar();
    while(c<'0'||c>'9'){if(c=='-') f=-1;c = getchar();}
    while(c>='0'&&c<='9'){x = c-'0'+x*10;c = getchar();}
    return x*f;
}
inline void write(ll x)
{
     if(x<0) putchar('-'),x=-x;
     if(x>9) write(x/10);
     putchar(x%10+'0');
}
ll Ex_gcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0){x=1;y=0;return a;}
    ll d = Ex_gcd(b,a%b,x,y);
    ll t = x;
    x = y;
    y = t - a/b*y;
    return d;
}
ll Ex_Crt()
{
    ll a = m[1],rr=r[1],x,y;
    for(int i=2;i<=n;++i)
    {
        ll b = m[i];
        ll c = r[i]-rr;
        ll d = Ex_gcd(a,b,x,y);
        c = c/d; b = b/d;
        x = (x*c%b+b)%b;
        ll lcm = a*b;
        rr = (x*a%lcm+rr)%lcm;
        a = lcm ;
    }
    return rr==0? a:rr;
}
int main()
{
    n = read();
    for(int i=1;i<=n;++i)
    {
        m[i] = read();
        r[i] = read();
    }
    write(Ex_Crt());
    return 0;
}

(3)POJ 1006

(4)POJ 2891

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值