目录
一、引入:古人奇妙的问题与解法。
有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?——《孙子算经》(所以中国剩余定理也称孙子定理)
翻译过来就是 “有一样东西不知道有多少,三个三个数剩二个,五个五个数剩三个,七个七个数剩二个。问有多少东西?”,且这个问题问最小值。若将它转化为符号与数字,也就是一组同余方程,如下。
注:我们这里设为东西的数量,下文也会提及。
二、步入正题:什么是中国剩余定理?
1.如何解决
(1)第一步:分解
这个问题我们可以拆解一下,变为……
- “三个三个数剩一个(1),五个五个数一个不剩(0),七个七个数同样一个不剩(0)。”
- “三个三个数一个不剩(0),五个五个数剩一个(1),七个七个数同样一个不剩(0)。”
- “三个三个数一个不剩(0),五个五个数同样一个不剩(0),七个七个数剩一个(1)。”
(2)第二步:计算
让我们先来解三个分解问题……以第一题为例,怎么解呢?以与0同余为突破口,已知为5和7的倍数也就是(最小公倍数)的倍数所以我们设,解同余方程,首先左侧对3取余得,即为,再次变换为,显然还需要求的逆元即,即。
其余以此类推,同理第二题为21,第三题为15。
(3)第三步:合并
有点意思~我们只需要按照原来的“三个三个数剩二个,五个五个数剩三个,七个七个数剩二个”变为(上文数值),但这数也未免太大了,所以还需要,但为什么对105取余?那是因为,通过取模来去掉不需要的部分(停下思考ing)。
(4)最终求出结果
结果为23,这就是中国剩余定理能解决的问题——解决求最小满足上文类型同余方程的定理。
注:相信你已经了解了许多,这里只是一些定理,若了解深刻,可自行跳过。
2.深入中国剩余定理
这,是一组同余方程,它共有个,且到两两互质用上文方法,一个一个开始“孤立”。
这样整个方程组只剩一个同余于1,其他为0,我们设为所有的乘积,还记得吗?我们分解成许多,现在我们通过式子将的值写出来,如下。
注:请谨慎辨别下方算式,(举个例子)当在模意义下且与不互质时,没有逆元!
有了 ,便可求得,好问题又来了,为什么没有?那是因为上文说过两两互质。
3.一道合适的自测题目
在洛谷中有一道非常合适的模板题,传送门在这,but我只有扩展中国剩余定理的的代码,不过通用,继续往下看吧。
三、扩展算法:扩展中国剩余定理(大数翻倍法)。
1.说出我的扩展的理由
首先我觉得很赞,首先他可以免除所有必须两两互质的悲伤事情。其次代码也很简单易懂,更适合编程宝宝体质。
2.定理原理:主要合并
首先有和两个同余方程,将其可合并为与(此为中的最小值)或(此为中的最小值)在模的意义下。
注:后面就是代码了,请放心食用😀。
3.附上代码:写的不好QAQ
而在洛谷中还有一道非常合适的模板题,传送门在这。代码如下……
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a/gcd(a,b)*b
void merge(ll &a,ll &pa,ll b,ll pb){
if(pa<pb)swap(pa,pb),swap(a,b);//小小的优化:交换,后面循环能更短。
ll l=lcm(pa,pb);//提前计算也是一个小小优化,让循环更短。
while(a<=l&&a%pb!=b)a+=pa;
if(a>l)a=0,pa=1;//在加一个特判前后呼应。
else pa=l;
}
ll n,x,p,sumx,sump=1;//x≡sumx(mod sump),即为最终合并的结果。
int main(){
scanf("%lld",&n);
for(ll i=1;i<=n;i++){
scanf("%lld%lld",&p,&x);
x%=p;
merge(sumx,sump,x,p);
}
printf("%lld",sumx);
}
如果去掉优化即为……
void merge(ll &a,ll &pa,ll b,ll pb){
while(a%pb!=b)a+=pa;
pa=lcm(pa,pb);
}
提一嘴,这一道题是道洛谷紫题,同样也是我的第一道紫题。
四、最后
中国文化博大精深,非常适合中国宝宝体质。