CRT&EXCRT

CRT

定义

CRT

证明

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;
}
posted @ 2018-11-06 19:24  lnyzo  阅读(147)  评论(0)    收藏  举报