CRT&EXCRT
CRT
定义

证明
比较简单
EXCRT
的确与CRT没什么关系,与扩展欧几里得比较有关系
定义
模数无限制(两两不强制互质)的多个同余方程求解
求解
假设我们这里有两个方程:
x=a1∗x1+b1
x=a2∗x2+b2
a1,a2是模数,b1,b2是余数。
那么我们可以合并这两个方程:
a1∗x1+b1=a2∗x2+b2,由于x1和x2可以取负无穷到正无穷,所以符号不能约束它们,我们随便变一变形得到:a1∗x1+a2∗x2=b2−b1
接下来扩展欧几里德!
好的,现在我们求出了一个最小正整数解x1,那么令k=(a1∗x1+b1)k=(a1∗x1+b1)。现在我们搞个新方程出来:
x≡k(mod lcm(a1,a2) )
那么一路合并下去就可以得到最终的解答了。
代码(poj2891):
#include<cstdio>
typedef long long ll;
const int N=1e5+5;
int n;
ll b[N],a[N];//x=a[i](%b[i])
ll exgcd(ll a,ll b,ll &x,ll &y){
if(!b){x=1,y=0;return a;}
ll d=exgcd(b,a%b,x,y),tmp=x;
x=y,y=tmp-(a/b)*y;
return d;
}
ll work(){
ll M=b[1],A=a[1],t,d,x,y;
for(int i=2;i<=n;i++){//之前的方程:x=A+M*y 现在的:x=a[i]+b[i]*y
d=exgcd(M,b[i],x,y);//M为之前所有模数的lcm 求解合并&移项A-a[i]=M*x+b[i]*y
if((a[i]-A)%d)return -1;//无解的情况
x*=(a[i]-A)/d,t=b[i]/d,x=(x%t+t)%t;//求最小正整数解,防溢出
A=M*x+A,M=M/d*b[i],A%=M;//合并成新的方程
}
A=(A%M+M)%M;return A;
}
int main(){
while(scanf("%d",&n)!=EOF){
for(int i=1;i<=n;i++)scanf("%lld%lld",&b[i],&a[i]);
printf("%lld\n",work());
}
return 0;
}
事实上我们发现在luogu中这样是要WA一个点的,要爆longlong,luogu的题解里有用慢速乘的
慢速乘和ksm差不多
#include<cstdio>
typedef long long ll;
const int N=1e5+5;
int n;
ll b[N],a[N];//x=a[i](%b[i])
ll exgcd(ll a,ll b,ll &x,ll &y){
if(!b){x=1,y=0;return a;}
ll d=exgcd(b,a%b,x,y),tmp=x;
x=y,y=tmp-(a/b)*y;
return d;
}
ll slowmul(ll n,ll k,ll mo){
ll ans=0;
for(;k;k>>=1,n=(n+n)%mo)if(k&1)ans=(ans+n)%mo;
return ans;
}
ll work(){
ll M=b[1],A=a[1],t,d,x,y;
for(int i=2;i<=n;i++){//之前的方程:x=A+M*y 现在的:x=a[i]+b[i]*y
ll B=((a[i]-A)%b[i]+b[i])%b[i];
d=exgcd(M,b[i],x,y);//M为之前所有模数的lcm 求解合并&移项A-a[i]=M*x+b[i]*y
if((a[i]-A)%d)return -1;//无解的情况
x=slowmul(x,B/d,b[i]);
//x*=(a[i]-A)/d;
t=b[i]/d,x=(x%t+t)%t;//求最小正整数解,防溢出
A=M*x+A,M=M/d*b[i],A%=M;//合并成新的方程
}
A=(A%M+M)%M;return A;
}
int main(){
while(scanf("%d",&n)!=EOF){
for(int i=1;i<=n;i++)scanf("%lld%lld",&b[i],&a[i]);
printf("%lld\n",work());
}
return 0;
}

浙公网安备 33010602011771号