exgcd的模板以及应用

基础概念

概念来源:https://blog.csdn.net/qq_49593247/article/details/125588288
裴蜀定理
若a,b是整数,且gcd(a,b)=d,那么对于任意的整数x,y,ax+by都一定是d的倍数,特别地,一定存在整数x,y,使ax+by=d成立。它的一个重要推论是:a,b互质的充分必要条件是存在整数x,y使ax+by=1.
Exgcd
针对于一次不定方程ax+by=c进行求解,利用以上的裴蜀定理可以进行求解,当然要满足 gcd(a,b)|c 这个前置情况这个时候实际上就是对于

b*x+(a%b)*y=d(辗转相除法求得)

a%b=a-(a/b)*b

进行带入即可得到:

ay-b(x-(a/b)*y)=d

和原式ax+by=d对比可以知道辗转相除的过程中:

x=y,y=x-(a/b)*y

模板

我们可以得到exgcd的过程为:

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

应用

同余方程

题目来源:https://www.luogu.com.cn/problem/P6610

思路

最经典的模板题:套用exgcd模板,然后套最小正整数模板(x%(b/d)+(b/d))%(b/d),因为本题的d(也就是gcd(a,b))为1,所以可以省略

编程

#include <iostream>
using namespace std;
#define int long long

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

signed main() {
	int a, b, x, y;
	cin >> a >> b ;
	exgcd(a, b, x, y);
	x = (x % b + b) % b ;
	cout << x << endl;
	return 0;
}

青蛙的约会

题目来源:https://www.luogu.com.cn/problem/P1516

思路

exgcd的变形,由题意我们可以得出x+km≡y+kn(mod l),那么x+km与y+kn跟l成倍数关系,x+km−(y+kn)=lz,z∈Z,变换一下可以得出x-y-k(n-m)= lz,我们令a=n-m,b=l,c=x-y,那么ka+bz=c,形如ax+by=c,令gcd(a,b)=d,通过exgcd我们可以得出ax1+by1=d,方程两边同时乘以c/d,可以得出acx1/d +bcy1/d= c,那么x=c*x1/d,然后套最小正整数模板(x%(b/d)+(b/d))%(b/d)即可,同时考虑gcd为正数的问题,当a<0时,a和c都要加上负数,至于无解的情况只有当c不是d的倍数才有可能(裴蜀定理)

编程

int exgcd(int a, int b, int &x1, int &y1) {
	if (b == 0) {
		x1 = 1, y1 = 0;
		return a;
	}
	int d = exgcd(b, a % b, y1, x1);
	y1 -= (a / b * x1);
	return d;
}
void solve(){
	int x,y,m,n,l,x1,y1;
	cin >> x >> y >> m >> n >> l;
	int a=n-m,b=l,c=x-y;
	if(a<0){
		a=-a;
		c=-c;
	}
	int d=exgcd(a,b,x1,y1);
	if(c%d){
		cout << "Impossible" << endl;
	}
	else{
		cout << (x1*c/d%(b/d)+(b/d))%(b/d) << endl;
	}
}

二元一次不定方程 (exgcd)

题目来源:https://www.luogu.com.cn/problem/P5656

思路

先套用exgcd模板求出最小的特解x,然后用裴蜀定理判断无解的情况,剩下的就是二元一次方程的解法(纯数学,本蒟蒻不会,可以参考 https://www.luogu.com.cn/article/4vzo8dr6 这位大佬的数学推导过程,写的很好)

编程

#include<bits/stdc++.h>
#define int long long 
#define endl "\n"
#define fi first
#define se second
#define PII pair<int,int> 
using namespace std;
const int N=2e5+5;
int exgcd(int a,int b,int &x,int &y)
{
    if(b==0)
    {
        x=1,y=0;
        return a;
    }
    int d=exgcd(b,a%b,y,x);
    y-=(a/b*x);
    return d;
}
void solve(){
	int a,b,c,x,y;
	cin >> a >> b >> c;
	int d=exgcd(a,b,x,y);
	if(c%d){
		cout << -1 << endl;
		return ;
	}
	x=x*c/d,y=y*c/d;
	int tx=b/d,ty=a/d;
	int k=ceil((1.0-x)/tx);
	x+=tx*k;
	y-=ty*k;	
	if(y<=0){
		int ymin=y+ty*1ll*ceil((1.0-y)/ty);
		cout << x << " " << ymin << endl;
	}
	else{
		cout << (y-1)/ty+1 << " " << x << " " << (y-1)%ty+1 << " " << x+(y-1)/ty*tx << " " << y << endl;
	}
	return ;
}
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int t=1;
	cin >> t;
	while(t--) solve();
	// cout << fixed;//强制以小数形式显示
    // cout << setprecision(n); //保留n位小数
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值