GCD 及 EXGCD 复习笔记

GCD

辗转相除法

证明

设实数 (a)(b) ,我们要证明 $$gcd(a,b) == gcd(b,a % b) $$

  1. 证明 $gcd(a,b) | (a-b) $

    可以发现,(a==km)(b==lm)((a-b)==(k-l)m quad(m in N^*))

    (gcd(a,b)==m),易知 $gcd(a,b) | (a-b) $ 。

    同理

    (a-b=kp)(b=lp) ,那么 (a=(k+l)p) ,所以 (gcd(a-b,b)|a)

  2. 由 $ 1 $ 可得,(gcd(a,b))(b,a-b) 的公因数,且 (gcd(a-b,b))(a,b) 的公因数。由 (gcd) 的定义可知 (gcd(a,b) leq gcd(b,a-b))(gcd(a,b) geq gcd(b,a-b))(gcd(a,b) == gcd(b,a-b))

  3. (a) 重复减去 (b) 其实就是 取模。

Code

很简单,直接贴了。

int gcd(int a,int b){
   return b==0?a:gcd(b,a%b);
}
  • 注意 (gcd) 只能处理正数,负数处理方法见线性同余方程。

  • 顺带一提,(lcm)(最小公倍数) ( imes gcd = a imes b)

luogu P1029

EXGCD

原理要用到 (gcd) ,但用处和 (gcd) 关联其实不是很大。

裴蜀定理

方程

[ax+by=gcd(a,b) ]

至少有一组整数解。

证明

不难看出问题可以简化成:

[ax+by=gcd(a,b) iff mx+ny=1 ]

其中,(m)(n) 互质。即证明两个互质的数可以线性组合出 (1)

(gcd) 的过程我们发现,辗转相除的实质就是更相减损,即用两数加减组合。即两个互质的数可以组合出 (1) (也就是任何数)。

exgcd

根据裴蜀定理,我们知道,对于一个二元一次方程:

[ax+by=c ]

(c)(gcd(a,b)) 的整数倍时,方程有整数解。

那么如何解这个方程呢?

原理

(gcd(a,b)|c) 时 ,我们将等式两边同除以 (frac{c}{gcd(a,b)})

[ax+by=c iff a imes frac{x}{c}+b imesfrac{y}{c}=gcd(a,b) ]

(frac{x}{c})(frac{y}{c})(x')(y'),根据 (gcd) 的原理我们知道:

[b imes x''+a\%b imes y''= gcd(b,a\% b)\ implies b imes x''+(a-b imes lfloorfrac{a}{b} floor) imes y''=gcd(b,a\%b)\ implies a imes y''+b imes (x''-lfloorfrac{a}{b} floor imes y'')=gcd(b,a\%b)=gcd(a,b)\ implies a imes y''+b imes (x''-lfloorfrac{a}{b} floor imes y'')=ax'+by' \ herefore x'=y''qquad y'=x''-lfloorfrac{a}{b} floor imes y'' ]

这样不断递归下去,直到:

[gcd(a,b) imes x'''+0 imes y'''=gcd(a,b)\ herefore x'''=1qquad y'''=0 ]

然后回溯回去求得 (x)(y)

Code

void exgcd(int a,int b,int &x,int &y){
	if(b==0){
		x=1;y=0;
	}
	else{
		exgcd(b,a%b,y,x);
		y-=(a/b)*x;
	}
}

线性同余方程*

母题

求解

[ax equiv b (mod p) ]

原式可以如下变换:

[(a imes x)\%p=b\%p iff (a imes x-b)\%p=0\ implies ax+b=py \ implies ax+p(-y)=b ]

这样我们就能用 (exgcd) 求解了。

这里要注意,(exgcd) 求解的是

[ax+p(-y)=gcd(a,p) ]

我们要给求出的解进行处理才行。

下面我们来上题

例题

luogu P1082 MOD

模板啦直接贴代码

#include<cstdio>
#include<algorithm>
using namespace std;

void exgcd(int a,int b,int &x,int &y){
	if(b==0){
		x=1;
		y=0;
	}
	else{
		exgcd(b,a%b,y,x);
		y-=(a/b)*x;
	}
}

int main()
{
	int a,b;
	int x,y;
	scanf("%d %d",&a,&b);
	//a,b互质 
	exgcd(a,b,x,y);
	printf("%d",((x%b)+b)%b);
	return 0;
}

[luogu P1516] 青蛙ha的约会

  1. 题目大意:

求满足 (x+k imes n equiv y+k imes m quad (mod ; l)) 的最小的 (k)

  1. 推柿子:

[(x+k imes n )\%l = (y+k imes m)\%l\ implies (x-y)+(n-m) imes k=l imes lambda\ implies (m-n) imes k+l imes lambda=x-y ]

然后几乎就转化成裸题辽。

  1. 细节

我们发现 (m-n) 可能是负数,但是 (exgcd) 只能处理正数,所以要小小变换一下。因为我们只需要知道 (k) ,就可以这样:

[(m-n) imes k+l imes lambda=x-y iff (n-m) imes k+l imes (-lambda)=y-x ]

  1. (Code)
#include<cstdio>
#include<algorithm>
typedef int int_;
#define int long long
using namespace std;

int n,m,wa,wb,l;

int mod(int o,int p){
	return ((o%p)+p)%p;
}

int gcd(int a,int b){
	return b==0?a:gcd(b,a%b);
}

void exgcd(int a,int b,int &x,int &y){
	if(b==0){
		x=1;y=0;
	}
	else{
		exgcd(b,a%b,y,x);
		y-=(a/b)*x;
	}
}



int_ main()
{
	scanf("%lld %lld %lld %lld %lld",&wa,&wb,&n,&m,&l);
	wa=mod(wa,l);wb=mod(wb,l);
	int a=m-n,b=l,c=wa-wb;
    if(a<0){
    	a=-a;
    	c=-c;
	}
	int g=gcd(a,b);
	if(c%g != 0){
		printf("Impossible");
		return 0;
	}
	a/=g;b/=g;c/=g;
	int x,y;
	exgcd(a,b,x,y);
	x*=c;
	x=mod(x,b);
	printf("%lld",x);
	return 0;
}

[luogu P2421] 荒岛野人

  1. 题目大意

和上题稍有不同,这题是两两求(c_i+k imes p_i equiv c_j+k imes p_jquad (mod; M))(min(l_i,l_j)) 内无正整数解。

  1. 做法

很简单,只需从小到大枚举(M) ,然后检验每两个人都不会相遇就ok。

#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn 1000000

int n,maxx,c[20],p[20],l[20];

int mod(int o,int M){
	return ((o%M)+M)%M;
}

int gcd(int a,int b){
	return b==0?a:gcd(b,a%b);
}

void exgcd(int a,int b,int &x,int &y){
	if(b==0){
		x=1,y=0;
	}
	else{
		exgcd(b,a%b,y,x);
		y-=(a/b)*x;
	}
}

bool work(int i,int j,int m){
	int a=p[i]-p[j],b=m,d=c[j]-c[i];
	if(a<0){
		a=-a,d=-d;
	}
	int g=gcd(a,b);
	if(d%g != 0){
		return false;
	}
	a/=g,b/=g,d/=g;
	int x,y;
	exgcd(a,b,x,y);
	x*=d;
	x=mod(x,b);
	if(x <= min(l[i],l[j])) return true;
	else return false;
}

bool check(int m){
	for(int i=1;i<n;i++){
		for(int j=i+1;j<=n;j++){
			if(work(i,j,m)){
				return false;
			}
		}
	}
	return true;
}




int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d %d %d",&c[i],&p[i],&l[i]);
		maxx=max(maxx,c[i]);
		c[i]--;
	}
	for(int i=1;i<=maxn;i++){
		if(check(i)){
			printf("%d",max(maxx,i));
			return 0;
		}
	}
	return 0;
}

内容采用“知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议”进行许可。请您在转载时注明来源及链接。


  • (frak byquad thorn\_)
原文地址:https://www.cnblogs.com/thornblog/p/11715805.html