GCD及EXGCD 复习笔记

GCD

辗转相除法

证明

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

  1. 证明 gcd(a,b)|(ab)

    可以发现,a==kmb==lm(ab)==(kl)m(mN)

    gcd(a,b)==m,易知 gcd(a,b)|(ab)

    同理,设 ab=kpb=lp ,那么 a=(k+l)p ,所以 gcd(ab,b)|a

  2. 由 1 可得,gcd(a,b)b,ab 的公因数,和上面相似可以证出 gcd(ab,b)a,b 的公因数。由 gcd 的定义可知 gcd(a,b)gcd(b,ab)gcd(a,b)gcd(b,ab)gcd(a,b)==gcd(b,ab)

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

Code

很简单,直接贴了。

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

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

luogu P1029

EXGCD

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

裴蜀定理

方程

ax+by=gcd(a,b)

至少有一组整数解。

证明

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

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

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

gcd 的过程我们发现,辗转相除的实质就是更相减损,即用两数线性组合。我们只要按照 gcd 过程倒推回来就行了,这个过程其实差不多就是 exgcd 了。

exgcd

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

ax+by=c

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

那么如何解这个方程呢?

原理

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

ax+by=ca×xc+b×yc=gcd(a,b)

xcycxy,根据 gcd 的原理我们知道:

b×x+a%b×y=gcd(b,a%b)b×x+(ab×ab)×y=gcd(b,a%b)a×y+b×(xab×y)=gcd(b,a%b)=gcd(a,b)a×y+b×(xab×y)=ax+byx=yy=xab×y

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

gcd(a,b)×x+0×y=gcd(a,b) x=1y=0

然后回溯回去求得 xy

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;
	}
}

线性同余方程*

母题

求解

axb (mod p)

原式可以如下变换:

(a×x)%p=b%p(a×xb)%p=0ax+b=pyax+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×ny+k×m(modl) 的最小的 k

  1. 推柿子:

(x+k×n)%l=(y+k×m)%l(xy)+(nm)×k=l×λ(mn)×k+l×λ=xy

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

  1. 细节

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

(mn)×k+l×λ=xy(nm)×k+l×(λ)=yx

  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. 题目大意

和上题稍有不同,这题是两两求 ci+k×picj+k×pj(modM)min(li,lj) 内无正整数解。

  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;
}

-EOF-
posted @   T_horn  阅读(358)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
点击右上角即可分享
微信分享提示