GCD
众所周知
有GCD(x,y)=GCD(x,y-x)
int GCD(int x,int y){
return y==0? x:GCD(y,x%y);
}
EXGCD
扩展欧几里得用于求一组特解(p,q)使得 pa+qb=GCD(a,b) (其中a和b为给定的常数)
根据数论相关定理,解一定存在
因为pa+qb=GCD(a,b)
所以pa+qb=GCD(b,a%b)=pb+qa%b=pb+q(a-a/bq)=qa+(p-a/bq)b
也就是说pa+qb=qa+(p-a/bq)*b
所以只要每次递归进行以下操作
int tmp=p;
p=q;
q=tmp-a/b*q;
递归边界和普通GCD一样的是,当b=0时回退。但还要加上一句话,x=1,y=0;
CODE:
inline ll exgcd(ll a,ll b,ll &xx,ll &yy){
if(!b){
xx=1;yy=0;
return a;
}
ll gcd=exgcd(b,a%b,xx,yy);
ll tmp=xx;
xx=yy;
yy=tmp-(a/b)*yy;
return gcd;
}
inv
即逆元,有什么用呢???
先来看一下定义
若a*x ≡1(mod b),且GCD(a,b)=1,则称x为a的逆元,记为a-1。
此方程等价于ax+by=1,又GCD(a,b)=1,即ax+by=GCD(a,b),则可以用扩展欧几里得求解。
void exgcd(int a,int b,int c,int &x,int &y){
if(!a){
x=0;y=c/b;
return;
}
int tx,ty;
exgcd(b%a,a,tx,ty);
x=ty-(b/a)*tx;
y=tx;
}
其中,x的值即所求逆元
当然,如果需要求多次求逆元,我们可以线性地预处理。
我们设p=k * i+r,则有k * i+r ≡ 0 (mod p)
k * r-1+i-1 ≡ 0 (mod p)
i-1 ≡ -k * r-1 (mod p)
i-1 ≡ -\([\frac{p}{i}]\)*(p mod i)-1 (mod p)
inv[i]=-(p/i)*inv[p%i];
求解线性同余方程
1.对于方程 a * x + b* y =c,该方程等价于 a * x ≡ c (mod b) , 有解的充分必要条件是 GCD(a,b) | c
2.若GCD(a,b)=1,且x0,y0为 a * x+b* y =c的一组特解,则该方程的任意解可以表示为 x=x0+bt,y=y0-at
特别的,若要求最小非负整数x,t=b/GCD(a,b),x=(x%t+t)%t
int exgcd(···){···}
bool solve(int a,int b,int c,int &x,int &y){
int d=exgcd(a,b,x,y);
if(c%d) return false;
int k=c/d;
x*=k;y*=k;
return true;
}