中国剩余定理,又名孙子定理
1、
首先:
能求解什么问题呢?
问题:
一堆物品
3个3个分剩2个
5个5个分剩3个
7个7个分剩2个
问这个物品有多少个
解这题,我们需要构造一个答案
我们需要构造这个答案
5*7*inv(5*7, 3) % 3 = 1
3*7*inv(3*7, 5) % 5 = 1
3*5*inv(3*5, 7) % 7 = 1
这是逆元
然后两边同乘你需要的数
2 * 5*7*inv(5*7, 3) % 3 = 2
3 * 3*7*inv(3*7, 5) % 5 = 3
2 * 3*5*inv(3*5, 7) % 7 = 2
a = 2 * 5*7*inv(5*7, 3)
b = 3 * 3*7*inv(3*7, 5)
c = 2 * 3*5*inv(3*5, 7)
那么
a % 3 = 2
b % 5 = 3
c % 7 = 2
其实答案就是a+b+c
因为
a%5 = a%7 = 0 因为a是5的倍数,也是7的倍数
b%3 = b%7 = 0 因为b是3的倍数,也是7的倍数
c%3 = c%5 = 0 因为c是3的倍数,也是5的倍数
所以
(a + b + c) % 3 = (a % 3) + (b % 3) + (c % 3) = 2 + 0 + 0 = 2
(a + b + c) % 5 = (a % 5) + (b % 5) + (c % 5) = 0 + 3 + 0 = 3
(a + b + c) % 7 = (a % 7) + (b % 7) + (c % 7) = 0 + 0 + 2 = 2
你看你看,答案是不是a+b+c(。・ω・)ノ゙,完全满足题意
但是答案,不只一个,有无穷个,每105个就是一个答案(105 = 3 * 5 * 7)
根据计算,答案等于233,233%105 = 23
如果题目问你最小的那个答案,那就是23了
另外一种解释:
一堆物品
3个3个分剩2个
5个5个分剩3个
7个7个分剩2个
问这个物品有多少个?
首先假如我们求出这样三个数k1,k2,k3,满足 k1与3互质且是5和7的倍数,k2与5互质且是3,7的倍数,k3与7互质且是3和5的倍数
那么容易意会得到,k1∗2+k2∗3+k3∗2 一定会是一个满足题目条件的数。
而题目的通解可表示为这个数每次都加上3,5,7的最小公倍数
辣么如何求出k1,k2,k3呢?
首先我们求出3,5,7的lcm=105
然后我们令:
x1=105/3=35
x2=105/5=21
x3=105/7=15
然后我们求解以下方程:
a∗x1%3=1 // a×x1是k1,a×x1%3=1,那么2×a×x1%3=2与题意相符
b∗x2%5=1
c∗x3%7=1
啊呀这个格式的式子好眼熟。。。那么就愉快地用扩展欧几里德求出来吧!
a=2,b=1,c=1 (用扩展欧几里德求出的)
答案就是:
ans=(a∗x1∗2+b∗x2∗3+c∗x3∗2)%lcm=23
其中
k1=a*x1=2*35=70
k2=b*x2=1*21=21
k3=c*x3=15*1=15;
所以满足条件的是 ( 70×2+21×3+15×2 ) %105 =23
结果是 23
2、
以下抄自百度百科
中国剩余定理给出了以下的一元线性同余方程组:
中国剩余定理说明:假设整数m1,m2, ... ,mn两两互质,则对任意的整数:a1,a2, ... ,an,
方程组(S)
有解,并且通解可以用如下方式构造得到:设
是整数m1,m2, ... ,mn的乘积,并设
是除了mi以外的n- 1个整数的乘积。设
这个就是逆元了
通解形式为
在模M的意义下,方程组(S)只有一个解:
Code:
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
LL a[101];
LL b[101];
LL ex_gcd(LL a,LL b,LL &x,LL &y,LL &d) // 求逆元
{
if(a==0&&b==0)
d=-1;
if(b==0)
{
x=1;
y=0;
d=a;
}
else{
ex_gcd(b,a%b,y,x,d);
y-=(a/b)*x;
}
}
LL CRT(LL n) // 中国剩余定理( CRT )
{
LL sum=1;
for(LL i=0;i<n;i++)
sum*=a[i]; // 最小公倍数
LL ans=0;
for(LL i=0;i<n;i++){
LL x,y,d;
LL t=sum/a[i];
ex_gcd(t,a[i],x,y,d); // 用扩展欧几里德 求 “系数”—— 1
ans=(ans+t*x*b[i])%sum; // 最后结果 ———————— 2
}
return (ans+sum)%sum;
}
int main()
{
LL k,n;
cin>>n;
for(LL i=0;i<n;i++)
scanf("%lld %lld",&a[i],&b[i]);
cout<<CRT(n)<<endl;
}
解释一下
1——: 求出来的是系数,类似于上边 1 中讲的 a、b、c
2——: 求的是最后的结果,直接套 这个式子 ans=(a∗x1∗2+b∗x2∗3+c∗x3∗2) // x1 是 t=sum / a[ i ]