[模板]扩展欧几里得


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;
}

当然,如果需要求多次求逆元,我们可以线性地预处理。

我们设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;
}

猜你喜欢

转载自www.cnblogs.com/wwlwQWQ/p/10492436.html