以前模模糊糊地学了扩欧,总是感觉不系统,今天好好整理了一下,应该算是把扩欧给弄透。
首先要先明白gcd(最大公约数),很重要的是辗转相除法,即gcd(a,b)=gcd(b,a%b)
板子是这样
int gcd(int a,int b) { if(b==0) return a; else return gcd(b,a%b); }
然后就是扩欧
根据裴蜀定理:
ax + by = m
有解当且仅当m是gcd的倍数。裴蜀等式有解时必然有无穷多个整数解,每组解x、y都称为裴蜀数,可用辗转相除法求得。
所以板子如下
int exgcd(int a,int &x1,int b,int &y1) { if(b==0) { x1=1; y1=0; return a; } int x2,y2; int gcd=exgcd(b,x2,a%b,y2); x1=y2; y1=x2-a/b*y2; return gcd; }
注意是&x和&y,参数的值是改变了的,最后求出的x,y的值实际上是ax+by=gcd(a,b)的解,且满足|x+y|最小
而对于要求的方程ax+by=c
若gcd|c则有解,不然无解,加一个判断就好
if(c%d) printf("No ");
而真正的x应该是x*(c/gcd),y应该是y*(c/gcd)
如果要求x或y的最小非负解该肿么办呢?
我们知道:ax+by=c,那么a(x+(b/gcd)*k)+b(y-(a/gcd)*k)=c
x,y的周期分别是b/gcd,a/gcd
所以在%的时候分别%一个b/gcd,a/gcd
下面是完整代码(要求求ax+by=c的x,y的最小非负解)
#include<bits/stdc++.h> #define ll long long using namespace std; ll exgcd(ll a,ll &x1,ll b,ll &y1) { if(b==0){y1=0;x1=1;return a;} ll x2,y2; ll gcd=exgcd(b,x2,a%b,y2); x1=y2; y1=x2-a/b*y2; return gcd; } int main() { freopen("modeq.in","r",stdin); freopen("modeq.out","w",stdout); int T; scanf("%d",&T); while(T--) { ll a,b,c; scanf("%I64d%I64d%I64d",&a,&b,&c); ll x,y; ll p=exgcd(a,x,b,y); if(c%p) printf("No "); else { ll q=c/p,m1=b/p,m2=a/p; ll xx=(x*q%m1+m1)%m1; ll yy=(y*q%m2+m2)%m2; printf("%I64d %I64d %I64d %I64d ",xx,(c-xx*a)/b,(c-yy*b)/a,yy); } } } /* 3 4 6 3 3 4 7 -2 3 6 */