中国剩余定理(又称 孙子定理)

博客图片都飞了,所以看的时候请多多担待。

中国剩余定理是数论中的一个关于一元线性同余方程组的定理,说明了一元线性同余方程组有解的准则以及求解方法。也称为孙子定理。

本文大部分使用的内容来自维基百科。

中国剩余定理说明:

这里写图片描述
题意:给出你n个ai和mi,最后让求出x的最小值是多少。

假设整数m1, m2, … , mn其中任两数互质,则对任意的整数:a1, a2, … , an,方程组 (S)有解,并且通解可以用如下方式构造得到:

1.设这里写图片描述是整数m1, m2, … , mn的乘积,并设
这里写图片描述,即 这里写图片描述是除了mi以外的n − 1个整数的乘积。

2.设这里写图片描述这里写图片描述这里写图片描述的数论倒数:这里写图片描述

3.方程组(S)的通解形式为:
这里写图片描述
在模 M的意义下,方程组 (S)只有一个解:这里写图片描述

**注:**对任何这里写图片描述,由于这里写图片描述,所以这里写图片描述说明存在整数 这里写图片描述使得这里写图片描述这样的这里写图片描述叫做 {\displaystyle M_{i}} M_{i}{\displaystyle m_{i}} m_{i}的数论倒数。

具体证明可以参照wiki 这里写链接内容

例子也可以看wiki中的“物不知数”。

以下给出一般情况下也就是整数m1, m2, … , mn其中任两数互质情况下的模板:

关于扩展欧几里得可以看以前的文章这里是传送门哦~

代码1:

m[]任意两个互质
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>

using namespace std;
#define LL long long
const int N=1e6+10;
const LL Me=1e9+7;
const int inf=0x3f3f3f3f;

int a[N],m[N];

LL exgcd(LL a,int b,LL &x,LL &y)
{
    if(a==0&&b==0)
        return -1;
    if(!b)
    {
        x=1;
        y=0;
        return a;
    }
    LL d=exgcd(b,a%b,y,x); //回代 注意这里的与扩展gcd的不同
    y-=a/b*x;
    return d;
}



LL CRT(int n)
{
    LL M=1;
    for(int i=0;i<n;i++)
        M*=m[i];
    LL ret=0;
    for(int i=0;i<n;i++)
    {
        LL x,y;
        LL tm=M/m[i];

        int xxx=exgcd(tm,m[i],x,y);
        ret=(ret+tm*x*a[i])%M;
    }
    return (ret+M)%M;
}



int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d%d",&m[i],&a[i]);
    }
    cout<<CRT(n)<<endl;

    return 0;
}

拓展:

中国剩余数定理是适用于n个mi两两互质的情况的,如果不互质呢,下面就是一个转换:
模不两两互质的同余式组可化为模两两互质的同余式组,再用孙子定理直接求解。
84=22×3×7,160=25×5,63=32×7,由推广的孙子定理可得 这里写图片描述这里写图片描述同解。

注意求解过程中应先检查同余式组上是否存在矛盾,存在矛盾的同余式组无解。
这里写图片描述

证明过程:

这里写图片描述

代码2:

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>

using namespace std;
#define LL long long
const int N=1e6+10;
const LL Me=1e9+7;
const int inf=0x3f3f3f3f;

int a[N],m[N];

LL exgcd(LL a,int b,LL &x,LL &y)
{
    if(a==0&&b==0)
        return -1;
    if(!b)
    {
        x=1;
        y=0;
        return a;
    }
    LL d=exgcd(b,a%b,y,x); //回代 注意这里的与扩展gcd的不同
    y-=a/b*x;
    return d;
}



LL CRT(int n)
{
    if(n==1)
    {
        if(m[0]>a[0])
            return a[0];
        else
            return -1;
    }
    LL x,y,d;
    for(int i=1; i<n; i++)
    {
        if(m[i]<=a[i])
            return -1;
        d=exgcd(m[0],m[i],x,y);
        if((a[i]-a[0])%d!=0)  不能整除则无解
            return -1;
        LL t=m[i]/d;
        x=((a[i]-a[0])/d*x%t+t)%t; 第0个与第i个模线性方程的特解
        a[0]=x*m[0]+a[0];
        m[0]=m[0]*m[i]/d;
        a[0]=(a[0]%m[0]+m[0])%m[0];
    }
    return a[0];
}



int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0; i<n; i++)
    {
        scanf("%d%d",&m[i],&a[i]);
    }
    cout<<CRT(n)<<endl;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值