版权声明:转载请留言 https://blog.csdn.net/qq_40744093 https://blog.csdn.net/qq_40744093/article/details/87967270
中国剩余定理(crt)
[用途]
求方程组中最小的非负整数解X
X ≡⎩⎪⎪⎨⎪⎪⎧a1(modm1)a2(modm2)...an(modmn)
其中
mi,mj两两互素
[引入]
韩信点兵:
韩信带1500名兵士打仗,战死四五百人,站3人一排,多出2人;站5人一排,多出4人;站7人一排,多出6人。韩信很快说出人数:1049。
即
X ≡⎩⎪⎪⎨⎪⎪⎧2(mod3)4(mod5)...6(mod7)
解得
X=1049
[结论]
X=∑1n(aitiMi)(modLcm)
其中:
最小公倍数
Lcm=m1m2...mn,因为
mi,mj两两互素
Mi=Lcm/mi
ti 为
Mi 模
mi意义下的乘法逆元
【证明】
其中的
M就是
Lcm
百度百科
最终就是
X=∑1n(aitiMi)(modLcm)
[代码]
typedef long long LL;
void exgcd(LL a,LL b,LL &x,LL &y)
{
if(!b){
x=1;
y=0;
return;
}
exgcd(b,a%b,y,x);
y-=a/b*x;
return;
}
LL crt(LL Lcm,LL len) /*Lcm:最小公倍数;len:m[]数组长度*/
{
LL ans=0;
for(int i=1;i<=len;++i){
LL Mi=Lcm/m[i]; /*j从1~len,除i外,所有m[j]的乘积*/
LL ti,y;
exgcd(Mi,m[i],ti,y); /*ti为Mi在模m[i]意义下的乘法逆元*/
ans=((ans+a[i]*ti*Mi)%Lcm+Lcm)%Lcm; /*结论*/
}
return ans;
}
拓展中国剩余定理(excrt)
与中国剩余定理不同的是 不一定 两两互素
[结论]
通解
X=x0(modLcm)
特解
x0=a1+t1m1
[证明]
以下出现的字母均为整数
余数:ai
模数:mi
假设:gcd(m1,mi)=gcd
x0={a1+tm1ai+timi
移向可得:
tm1+(−ti)mi=ai−a1
根据不定方程(丢番图方程):
二元一次不定方程的一般形式为ax+by=c。其中 a,b,c 是整数,ab ≠ 0。此方程有整数解的充分必要条件是a、b的最大公约数整除c。设x,y是该方程的一组整数解,那么该方程的通解可表示为
x=x0+gcd(a,b)bt;y=y0−gcd(a,b)bt;
所以对于
tm1+(−ti)mi=ai−a1
t1有解则有:
(ai−a1)÷gcd=0
则
t=t0+gcdmik
即
t≡t0(modgcdmi)
对
Tm1+(−Ti)mi=gcd先用拓展欧几里得求
T
进而
t=Tgcdai−a1(modgcdm[i])
则特解
x0=a1+tm1
对于通解X与特解
x0满足:
{X=a1+k1m1x0=a1+k2m1
{X=a2+k3m2x0=a2+k4m2
上式减去下式得
X−x0=(k1−k2)m1=(k3−k4)m2
即
X−x0=K1m1=K2m2
假设
n1=gcdm1,n2=gcdm2,显然
gcd(n1,n2)=1;
有
K1n1=K2n2
则
K1=n1K2n2
而
gcd(n1,n2)=1,K1为整数
所以
K2%n1=0
两边同乘
n2gcd
K2n2gcd%n1n2gcd=0
而
X−x0=K2m2=K2n2gcd
n1n2gcd=gcdn1gcdn2gcd=Lcm(m1,m2)=Lcm
所以
(X−x0)%Lcm=0
即通解
X≡x0(modLcm)
[代码]
#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;
const LL N=1e5+9;
LL a[N],m[N];
LL ksc(LL x,LL y,LL mod)
{
return (x*y-(LL)((long double)x*y/mod)*mod+mod)%mod;
}
LL exgcd(LL a,LL b,LL &x,LL &y)
{
if(!b){
x=1;
y=0;
return a;
}
LL gcd=exgcd(b,a%b,y,x);
y-=a/b*x;
return gcd;
}
LL excrt(LL len)
{
LL x=a[1],Lcm=m[1],t,y; /*一个方程时,x=a[1],Lcm=m[1]*/
for(int i=2;i<=len;++i){
a[i]-=x; /*求a[i]-a[1]*/
LL gcd=exgcd(Lcm,m[i],t,y); /*求gcd与证明过程中的T*/
if(a[i]%gcd)return -1; /*方程无解*/
t=ksc(t,a[i]/gcd,m[i]/gcd); /*O(1)快速乘求t*/
x+=t*Lcm; /*求特解x0*/
Lcm*=m[i]/gcd; /*更新Lcm*/
x=(x%Lcm+Lcm)%Lcm; /*更新通解*/
}
return x; /*均按照上述证明步骤*/
}
int main()
{
ios::sync_with_stdio(false);
LL n;
while(cin>>n){
memset(a,0,sizeof(a));
memset(m,0,sizeof(m));
for(int i=1;i<=n;++i)cin>>m[i]>>a[i];
cout<<excrt(n)<<endl;
}
return 0;
}