博客图片都飞了,所以看的时候请多多担待。
中国剩余定理是数论中的一个关于一元线性同余方程组的定理,说明了一元线性同余方程组有解的准则以及求解方法。也称为孙子定理。
本文大部分使用的内容来自维基百科。
中国剩余定理说明:
题意:给出你n个ai和mi,最后让求出x的最小值是多少。
假设整数m1, m2, … , mn其中任两数互质,则对任意的整数:a1, a2, … , an,方程组 (S)有解,并且通解可以用如下方式构造得到:
1.设是整数m1, m2, … , mn的乘积,并设
,即 是除了mi以外的n − 1个整数的乘积。
2.设为模的数论倒数:
3.方程组(S)的通解形式为:
在模 M的意义下,方程组 (S)只有一个解:
**注:**对任何,由于,所以说明存在整数 使得这样的叫做 模 的数论倒数。
具体证明可以参照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;
}