基础概念
概念来源: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;
}