『筆記』擴展歐幾里得(exgcd)

定義

擴展歐幾里得算法是用來在已知一組 \((a,b)\) 的時,求解一組 \((x,y)\) 使得ios

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

思想 and 板子

根據相關的知識能夠獲得算法

\[gcd(a,b)=gcd(b,a \bmod \ b) \]

由當 \(b=0\) 是便可得出 \(x=1,y=0\)ui

便可遞推求解spa

如何來作?code

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

\[bx+(a \bmod\ b)y=gcd(b,a \bmod b) \]

\[bx+(a-\lfloor\frac{a}{b}\rfloor\times b)\times y=gcd(b,a\bmod b) \]

\[bx+ay-\lfloor\frac{a}{b}\rfloor\times by=gcd(b,a\bmod b) \]

\[ay+b(x-\lfloor\frac{a}{b}\rfloor\times y)=gcd(b,a\bmod b) \]

進而能夠得出結論get

\[ax+by \]

\[\to ay+b(x-\lfloor\frac{a}{b}\rfloor\times y) \]

有告終論就能夠實現簡單的求解string

int exgcd(int a,int b)
{
	if(!b)
	{
		x=1;
		y=0;
		return a;
	}
	int gcd=exgcd(b,a%b);
	int tmp=x;
	x=y;
	y=tmp-a/b*y;
	return gcd;
}

爲何要用?it

在當前個人理解範圍內,它既能夠求出最大公約數,而且求出來的 \(x,y\) 能夠進行解決一些問題io

應用

可用於進行對二元一次不定方程的求解class

\[ax+by=c \]

根據裴蜀定理可得,這個方程有整數解的充要條件爲

\[c \ \bmod\ gcd(a,b)=0 \]

一樣,該方程能夠等同於

\[ax \equiv c\ (\bmod b) \]

P5656 板子能夠爲較爲拓展的應用

一、如何來求 \(ax+by=c\) 的一個解

首先咱們在有整數解的狀況下求出

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

的解 \((x_0,y_0)\)

\(k=\frac{c}{gcd(a,b)}\)

得出來

該方程的一個解能夠爲

\(\begin{cases} x=kx_0\\y=ky_0 \end{cases}\)

也就是方程在同除完 \(k\) 後求出解來,進而乘上 \(k\) 得出當前的解,由於\(a,b\)是不變的

二、如何根據一組解求出多組解來

首先任意選擇一個數\(t\),爲了方便敘述和計算,通常設\(t=1\)

設兩個整數 \(m,n\)

咱們能夠得出

\[a(x+m)+b(y+n)=c \]

\[ax+am+by+bn=c \]

\[ax+by=c \]

可知

\[am+bn=0 \]

那麼就是尋找一組 \((m,n)\)使得該方程 成當即可

能夠獲得

\(\begin{cases} m=\frac{b}{gcd(a,b)}\\n=-\frac{a}{gcd(a,b)} \end{cases}\)

代進去等式成立,進而就能夠獲得一組解

\(\begin{cases} x_i=x+m\\y_i=y+n \end{cases}\)

三、考慮正整數解的最大值最小值,及其正整數解的個數

首先設置一下變量

\(\begin{cases} d=gcd(a,b)\\tx=\frac{b}{d} \\ty=\frac{a}{d} \end{cases}\)

首先那咱們從x的最小值開始找起

能夠獲得式子

\[x+k\times tx\ge 1 \]

\[k\ge \lceil\frac{1-x}{tx}\rceil \]

獲得\(x_{min}=x+\lceil\frac{1-x}{tx}\rceil\times tx\)

那麼此時一樣能夠獲得 \(y_{max}=y-\lceil\frac{1-x}{tx}\rceil\times ty\)

  • 若是此時沒有\(y_{max}\)不是正整數的話

那麼就在求一下

\[y_{max}+k\times ty\ge1 \]

\[k\ge \lceil\frac{1-y_{max}}{ty}\rceil \]

那麼 \(y\) 的最小正整數解即爲 \(y_{max}+\lceil\frac{1-y_{max}}{ty}\rceil\times ty\)

一樣適用於有正整數解狀況下的\(y_{min}\)

  • 若是此時有正整數解

能夠很顯然的發現\(y_{min}=y_{max}\bmod ty\)

利用狀況一中的結論能夠求出

\[x_{max}=x_{min}-\lceil\frac{1-y_{max}}{ty}\rceil\times tx \]

至於解的個數能夠參考在\(y_{max}\to y_{min}\)過程當中 $ k $的變化

正整數解的組數即爲 \(\lfloor\frac{y_{max}-1}{ty}\rfloor+1\)

代碼請參考P5656

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#define int long long
using namespace std;
int x,y;//用於求解 
int read()//不到卡常不用快讀=_= 
{
	char c=getchar();
	int f=1;
	int x=0;
	while(c<'0'||c>'9')
	{
		if(c=='-')
			f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9')
	{
		x=(x<<1)+(x<<3)+(c^'0');
		c=getchar();
	}
	return x*f;
}
int exgcd(int a,int b)
{
	if(!b)
	{
		x=1;
		y=0;
		return a;
	}
	int ans=exgcd(b,a%b);
	int tmp=x;
	x=y;
	y=tmp-a/b*y;
	return ans;
}
signed main()
{
	int t;
	t=read();
	while(t--)
	{
		int a,b,c;
		a=read();
		b=read();
		c=read();
		int d=exgcd(a,b);
		if(c%d!=0)//根據裴蜀定理得出,c不是gcd(a,b)的倍數,則必定無整數解 
		{
			cout<<-1<<endl;
			continue;
		}
		x=x*c/d;
		y=y*c/d;//求出一組正整數解
		int tx=b/d;
		int ty=a/d;
		int k=ceil((1.0-x)/tx);//求出x_min正整數;判\斷y是否爲正整數 
		x+=k*tx;//x_min
		y-=k*ty;//求出此時的ymax 
		if(y<=0)
		{
			int ymin=y+ty*ceil((1.0-y)/ty)*1ll;
			printf("%lld %lld\n",x,ymin); 
		}
		else 
		{
			printf("%lld ",(y-1)/ty+1);//個數 
			printf("%lld ",x);//最小值 
			printf("%lld ",(y-1)%ty+1);//最小值 
			printf("%lld ",x+((y-1)/ty*tx));//max 
			printf("%lld \n",y);//max
		}
		continue;
	} 
	return 0;
}
相關文章
相關標籤/搜索