扩展欧几里得

终于开了数论课了,,再也不是只贴模板了,稍微懂了点原理,先来一发链接

点我刷Exgcd


(里面的最后一题是孙子定理,也是模板题)


---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

先贴几个最常见的模板:


最大公约数(辗转相除法)

#define ll long long
ll gcd(ll a,ll b){
	return b?gcd(b,a%b):a;
}

扩展欧几里得:求ax+by=gcd(a,b)中的x,y,其中d=gcd(a,b)

#define ll long long
ll exgcd(ll a,ll b,ll &x,ll &y){
	ll d;
	if (b==0){
		x=1;y=0;return a;
	}
	d=exgcd(b,a%b,y,x);
	y-=a/b*x;
	return d;
}

孙子定理的模板:

其中a[]是各个除数,m[]是各个余数,n是同余式子的个数,学完同余再来解释模板原理

ll CRT(ll a[],ll m[],ll n){
	ll M=1,d,ret=0,i,x,y,Mi,temp;
	for(i=0;i<n;i++) M*=m[i];
	for(i=0;i<n;i++){
		Mi=M/m[i];
		d=Exgcd(m[i],Mi,x,y);
		temp=mod_mul(y,Mi,M);
		temp=mod_mul(temp,a[i],M);
		ret=(ret+temp)%M;
	}
	return (M+ret)%M;
}

扩展欧几里得的最经典应用:

解决一次不定方程ax+by=c

(1)ax+by=c有解的充分必要条件是(a,b)=c

(2)利用公式的x0,y0得出一组特解,然后所有解为x=x0+b*k/gcd(a,b)    y=y0-a*k/gcd(a,b)

(3)题中有时候是求出x或者y的最小正整数解,利用通式取余即可



下面是各个题的代码:

A:

ll x,y,n,m,l;
ll a,b,g,mul;

int main(){
	//input;
	while(scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&n,&l)!=EOF){
		g=exgcd(m-n,-1*l,a,b);
		if ((y-x)%g){
			puts("Impossible");
			continue;
		}
		a*=(y-x)/g;
		mul=-1*l/g;
		//printf("%lld %lld\n",a,mul);
		a=(a%mul+mul)%mul;
		printf("%lld\n",a);
	}
	return 0;
}


B:

int main(){
	//input;
	int i,j,k;
	pow2[0]=1;
	for(i=1;i<=35;i++) pow2[i]=2LL*pow2[i-1];
	//for(i=1;i<=35;i++) printf("%lld\n",pow2[i]);
	while(scanf("%lld%lld%lld%d",&a,&b,&c,&k)!=EOF){
		if (k==0) break;
		if (a==b){
			puts("0");
			continue;
		}
		g=exgcd(c,pow2[k],x,y);
		if ((b-a)%g){
			puts("FOREVER");
			continue;
		}
		//printf("%lld\n",g);
		x*=(b-a)/g;
		l=pow2[k]/g;
		//printf("%lld %lld\n",x,l);
		x=(x%l+l)%l;
		printf("%lld\n",x);
		//cout<<endl;
	}
	return 0;
}

C:范围很小,直接根据题意for循环暴力即可


D:

ll a,b,g;
ll l,r;
ll x,y;

int main(){
	//input;
	while(scanf("%I64d%I64d",&a,&b)!=EOF){
		g=exgcd(a,b,x,y);
		if (g!=1){
			puts("sorry");
			continue;
		}
		x=(x%b+b)%b;
		y=(1-a*x)/b;
		printf("%I64d %I64d\n",x,y);
	}
	return 0;
}


E:

ll x,y,n,m,l;
ll a,b,g,mul;

int main(){
	//input;
	while(scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&n,&l)!=EOF){
		g=exgcd(m-n,l,a,b);
		if ((y-x)%g){
			puts("Pat");
			continue;
		}
		a*=(y-x)/g;
		mul=-1*l/g;
		if (mul<0) mul=-1*mul;
		//printf("%lld %lld\n",a,mul);
		a=(a%mul+mul)%mul;
		printf("%lld\n",a);
	}
	return 0;
}

F:

int main(){
	//input;
	while(scanf("%I64d%I64d%I64d",&a,&b,&m)!=EOF){
		if (a+b+m==0) break;
		tot=ansx=ansy=INF;
		tot*=99999999;

		g=exgcd(a,-1*b,x,y);
		x*=m/g;
		y*=m/g;
		l=b/g;if (l<0) l=-1*l;
		r=a/g;if (r<0) r=-1*r;
		x1=(x%l+l)%l;
		y=(y%r+r)%r;
		x2=(m+b*y)/a;
		for(x=x1;x<=x2;x+=l){
			y=(a*x-m)/b;
			if (y<0) continue;
			if (x+y<=ansx+ansy&&tot>=x*a+b*y){
				ansx=x;
				ansy=y;
				tot=x*a+b*y;
			}
		}
		
		g=exgcd(a,b,x,y);
		x*=m/g;
		y*=m/g;
		l=b/g;if (l<0) l=-1*l;
		r=a/g;if (r<0) r=-1*r;
		x1=(x%l+l)%l;
		y=(y%r+r)%r;
		x2=(m+b*y)/a;
		for(x=x1;x<=x2;x+=l){
			y=(m-a*x)/b;
			if (y<0) continue;
			if (x+y<=ansx+ansy&&tot>=x*a+b*y){
				ansx=x;
				ansy=y;
				tot=x*a+b*y;
			}
		}
		
		
		m=-1*m;
		g=exgcd(a,-1*b,x,y);
		x*=m/g;
		y*=m/g;
		l=b/g;if (l<0) l=-1*l;
		r=a/g;if (r<0) r=-1*r;
		x1=(x%l+l)%l;
		y=(y%r+r)%r;
		x2=(m+b*y)/a;
		for(x=x1;x<=x2;x+=l){
			y=(a*x-m)/b;
			if (y<0) continue;
			if (x+y<=ansx+ansy&&tot>=x*a+b*y){
				ansx=x;
				ansy=y;
				tot=x*a+b*y;
			}
		}
		printf("%I64d %I64d\n",ansx,ansy);
	}
	return 0;
}

G:孙子定理模板应用没什么好说的。。。。


-------------------------------------------------------------------------------------------------------------------------------------------------------------------

来几篇好的欧几里得的原理介绍的博客:

http://www.cnblogs.com/frog112111/archive/2012/08/19/2646012.html


http://www.acmerblog.com/extend-gcd-5610.html



看完这几个,刷完这几个题,模板弄下来,就基本没什么问题了。。这是最基本的内容了,学了同余等知识之后会有更多的结论

你可能感兴趣的:(ACM)