060.裴蜀定理 + 拓展欧几里得

裴蜀定理

如果a和b是不全为0的整数,则有整数x,y,使得 ax + by =gcd(a,b)

推论:

  1. ax + by = c 有解 , 当且仅当, c 是gcd(a,b)的倍数

  2. ax + by = 1 有解 , 当且仅当 ,gcd(a,b) = 1 ,即a与b互质

  3. 可由两项推广到多项

拓展欧几里得

目标 : 解 ax + by = gcd(a,b)

//返回值为gcd(a, b),同时求出满足ax+by=gcd(a,b)的一组解x, y

int ex_gcd(int a,int b,int &x,int &y){//x,y传引用
    if(b==0){
        x=1,y=0;
        return a;
    }
    int g=ex_gcd(b,a%b,y,x);
    y-=a/b*x;
    return g;
}

同余方程

求x使得 ax = b (mod p)

等价于求 ax + py = b 的一个解

转化为求 二元一次不定方程

特殊地,当 b=1 等价为求 a 在(mod p)意义下的逆元

解二元一次不定方程

一般的 对于 ax + by = c

有解判定

根据 裴蜀定理,方程有解当且仅当 gcd(a,b) | c , 即 c 是 gcd(a,b)的倍数

求特解

根据 拓展欧几里得 先求 ax + by = gcd(a,b) 的一组解 x0,y0

则原方程的一组特解为 :X ,Y

  • X = x0 * c / gcd(a,b)

  • Y = y0 * c / gcd(a,b)

求通解

参数 kx , ky

  • kx = b / gcd(a,b)

  • ky = a / gcd(a,b)

通解 x , y

  • x = X + k * kx

  • y = Y - k * ky

  • k 是整数

最小正整数解

  • kx = b / gcd(a,b)

  • 最小正整数解 x = ( X % kx + kx ) % kx

习题

01 裴蜀定理

luogu P4549

根据推论1,ax + by = c 有解,c 的最小值为 gcd(a,b)

根据推论3, 本题就是求所有数的最大公约数

int gcd(int a,int b){
    return b==0?a:gcd(b,a%b);
}
void solve(){
    int n,a;
    cin>>n;
    int ans=0;
    while(n--){
        cin>>a;
        ans=gcd(ans,abs(a));
    }
    cout<<ans;
}

02 同余方程

luogu P1082

int ex_gcd(int a,int b,int &x,int &y){
    if(b==0){
        x=1,y=0;
        return a;
    }
    int g=ex_gcd(b,a%b,y,x);
    y-=a/b*x;
    return g;
}
int inv(int b,int p){
    int x,y;
    ex_gcd(b,p,x,y);
    return (x%p+p)%p;
}
void solve(){
    int  a,b,x,y;
    cin>>a>>b;
    cout<<inv(a,b);
}

03 洗牌

luogu P2054

观察 1 2 3 4 5 6

操作 0 次 1 2 3 4 5 6

操作 1 次 4 1 5 2 6 3

操作 2 次 2 4 6 1 3 5

操作 3 次 1 2 3 4 5 6

跟踪 1 的位置变化 1 2 4 1

跟踪 2 的位置变化 2 4 1 2

跟踪 3 的位置变化 3 6 5 3

跟踪 4 的位置变化 4 1 2 4

跟踪 5 的位置变化 5 3 6 5

跟踪 6 的位置变化 6 5 3 6

发现每次下标变为原来的两倍,超出范围就对7取模

牌面为x的牌 洗牌 m次 后的位置就是 x * (2^m) % p,p=n+1

根据题意求 x * (2^m) % p = L , 其中 p = n-1

这等价于求 (2^m) * x + (n + 1) * y = L

由题意 n+1 为奇数,(2^m) 为偶数 ,两者互质gcd(n + 1, (2^m)) = 1

(2^m) * x0 + (n + 1) * y0 = 1

则有原方程的解 x = x0 * L

数据规模达到10^10,相乘会溢出,挂快速幂,龟速乘

typedef long long ll;

ll ex_gcd(ll a,ll b,ll &x,ll &y){
    if(b==0){
        x=1,y=0;
        return a;
    }
    ll g=ex_gcd(b,a%b,y,x);
    y-=a/b*x;
    return g;
}
ll mul(ll a,ll b,ll p){
    ll res=0;
    while(b){
        if(b&1)res=(res+a)%p;
        a=(a<<1)%p;
        b>>=1;
    }
    return res;
}
ll qpow(ll b,ll e,ll p){
    ll res=1;
    b%=p;
    while(e){
        if(e&1)res=mul(res,b,p);
        b=mul(b,b,p);
        e>>=1;
    }
    return res;
}
void solve(){
    ll n,m,k;
    cin>>n>>m>>k;

    ll x,y;
    ll p=n+1;
    ll a=qpow(2,m,p);

    ex_gcd(a,p,x,y);
    x=(x%p+p)%p;
    
    cout<<mul(x,k,p);
}

04 二元一次不定方程

luogu P5656

ll ex_gcd(ll a,ll b,ll &x,ll &y){
    if(b==0){
        x=1,y=0;
        return a;
    }
    ll g=ex_gcd(b,a%b,y,x);
    y-=a/b*x;
    return g;
}
ll a,b,c,x,y;
void solve(){
    cin>>a>>b>>c;
    ll g=ex_gcd(a,b,x,y);
    if(c%g!=0){
        cout<<"-1\n";
    }
    else{
        ll kx=b/g,ky=a/g;
        x*=c/g;
        if(x>0){
            ll k=(x-1)/kx;
            x-=k*kx;
        }
        else{
            ll k=(1-x+kx-1)/kx;
            x+=k*kx;
        }
        y=(c-a*x)/b;
        if(y<=0){
            ll k=(1-y+ky-1)/ky;
            cout<<x<<' '<<y+k*ky<<'\n';
        }
        else{
            ll k=(y-1)/ky;
            cout<<k+1<<' '<<x<<' '<<y-k*ky<<' '<<x+k*kx<<' '<<y<<'\n';
        }
    }
}

05 青蛙的约会

luogu P1516

ll ex_gcd(ll a,ll b,ll &x,ll &y){
    if(b==0){
        x=1,y=0;
        return a;
    }
    ll g=ex_gcd(b,a%b,y,x);
    y-=a/b*x;
    return g;
}

ll n,m,vn,vm,L,x,y,a,b,c;

void solve(){
    cin>>n>>m>>vn>>vm>>L;
    if(vn==vm){
        cout<<"Impossible";
        return;
    }
    if(n>m){
        if(vm>vn){
            a=vm-vn;
            c=n-m;
        }
        else{
            a=vn-vm;
            c=L-(n-m);
        }
    }
    else{
        if(vn>vm){
            a=vn-vm;
            c=m-n;
        }
        else{
            a=vm-vn;
            c=L-(m-n);
        }
    }
    b=L;
    ll g=ex_gcd(a,b,x,y);
    if(c%g!=0){
        cout<<"Impossible";
    }
    else{
        ll kx=b/g;
        x*=c/g;
        if(x>0){
            ll k=(x-1)/kx;
            x-=k*kx;
        }
        else{
            ll k=(1-x+kx-1)/kx;
            x+=k*kx;
        }
        cout<<x;
    }
}

06 格点问题

给定两个点A(x1,y1),B(x2,y2)

求A,B的连线上经过几个格点(含A,B)

结论:ans = gcd(abs(x1-x2),gcd(y1-y2)) + 1

07 无法组成的最大值

luogu P3951

a、b为硬币面额(>0),x、y为对应数量,c 为组成的金额

则有 ax + by = c

金额 c 合法,当且仅当 a、b 均非负

不妨设 a > b

为了得到最大的非法 c ,我们希望 x,y 都尽可能大

对 y :

当 y>=0 时,c 是合法的

所以 当 y=-1 时,c 非法且最大

对 x :

因为 ax + by = c ,即 c = ax (mod b)

所以 x 可以取 [ 0 , b )

x 取最大 (b-1) ,此时 a * (b-1) + b * (-1) = c ,即 c = ab-a-b

08 倒酒

luogu P1292

ll ex_gcd(ll a,ll b,ll &x,ll &y){
    if(b==0){
        x=1,y=0;
        return a;
    }
    ll g=ex_gcd(b,a%b,y,x);
    y-=a/b*x;
    return g;
}
void solve(){
    ll a,b,x,y;
    cin>>a>>b;
    ll g=ex_gcd(a,b,x,y);
    a=-a;
    x=-x;
    int kx=b/g;
    int ky=-a/g;
    while(x<0||y<0){
        if(x<0)x+=kx;
        else y+=ky;
    }
    cout<<g<<'\n';
    cout<<x<<" "<<y;
}
posted @ 2026-01-24 23:41  射杀百头  阅读(3)  评论(0)    收藏  举报