中国剩余定理
在《孙子算经》中有这样一个问题:“今有物不知其数,三三数之剩二(除以3余2),五五数之剩三(除以5余3),七七数之剩二(除以7余2),问物几何?”这个问题称为“孙子问题”,该问题的一般解法国际上称为“中国剩余定理”。具体解法分三步:
- 找出三个数:从3和5的公倍数中找出被7除余1的最小数15,从3和7的公倍数中找出被5除余1 的最小数21,最后从5和7的公倍数中找出除3余1的最小数70。
- 用15乘以2(2为最终结果除以7的余数),用21乘以3(3为最终结果除以5的余数),同理,用70乘以2(2为最终结果除以3的余数),然后把三个乘积相加15∗2+21∗3+70∗2得到和233。
- 用233除以3,5,7三个数的最小公倍数105,得到余数23,即233%105=23。这个余数23就是符合条件的最小数。
类比古人 我们假设n1除以3余2,n2除以5余3,n3除以7余2;
且n1+n2+n3满足
- 使n1+n2+n3的和满足除以3余2,n2和n3必须是3的倍数。
- 使n1+n2+n3的和满足除以5余3,n1和n3必须是5的倍数。
- 使n1+n2+n3的和满足除以7余2,n1和n2必须是7的倍数。
因此,为使n1+n2+n3的和作为“孙子问题”的一个最终解,需满足:
1.n1 除以3余2,且是5和7的公倍数。
2.n2除以5余3,且是3和7的公倍数。
3.n3 除以7余2,且是3和5的公倍数。
再使用逆元 即可求解
有整数解(mi两两互质)
Mi为M/mi; M i − 1 Mi^{-1} Mi−1为Mi的逆元
crt 板子
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
ll a[150],b[150],n,m=1,ans=0;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
ll d;
if(b==0)
{
x=1;y=0;return a;
}
else
{
d=exgcd(b,a%b,x,y);
ll t=x;x=y;y=t-a/b*y;
return d;
}
}
ll x,y,mi,d;
int main()
{
scanf("%lld",&n);
for(ll i=1;i<=n;i++)
{
scanf("%lld%lld",&a[i],&b[i]);m*=a[i];
}
for(ll i=1;i<=n;i++)
{
mi=m/a[i];
d=exgcd(mi,a[i],x,y);
ans=((ans+mi*x*b[i])%m+m)%m;
}
printf("%lld\n",ans);
return 0;
}
excrt
excrt Code 板子
#include<bits/stdc++.h>
#define ll __int128
using namespace std;
ll x,y,d,n;
void exgcd(ll a,ll b,ll &x,ll &y){
if(!b) d=a,x=1,y=0;
else exgcd(b,a%b,y,x),y-=a/b*x;
}
ll gcd(ll a,ll b){
return b?gcd(b,a%b):a;
}
ll lcm(ll a,ll b){
return a/gcd(a,b)*b;
}
ll a,b,A,B;
void merge(){
exgcd(a,A,x,y);
ll c=B-b;
if(c%d) puts("-1"),exit(0);
x=(x*c/d+A/d)%(A/d);
ll mod=lcm(a,A);
b=(a*x+b+mod)%mod;
a=mod;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
long long u,v;
scanf("%lld%lld",&u,&v);
A=u,B=v;
if(i>1) merge();
else a=A,b=B;
}
printf("%lld\n",(long long)(b%a));
}