洛谷 P5656 【模板】二元一次不定方程(exgcd)

给一个二元一次不定方程

ax+by=c ax+by=c

  • 无整数解:输出 -1
  • 有整数解:
    • 有正整数解:输出解的个数,输出 x,yx,y 的最小值和最大值
    • 无正整数解:输出 x,yx,y 的最小正整数值

思路:

首先是欧几里得算法,用来求 gcd(a,b)\mathrm{gcd}(a,b),首先假设 a>ba>b,假如 bab\mid a,则 gcd(a,b)=b\mathrm{gcd}(a,b)=b,假如 bab\nmid a,则:

a=kb+c a=kb+c

da,dbd\mid a,d\mid b,则显然有 dcd\mid c ,令 d=gcd(a,b)d=\mathrm{gcd}(a,b),则 gcd(a,b)c\mathrm{gcd}(a,b)\mid c,而 c=amodbc=a\bmod b,因此 gcd(a,b)=gcd(b,amodb)\mathrm{gcd}(a,b)=\mathrm{gcd}(b,a\bmod b)

int gcd(int a,int b){
	if(!b)return a;
	return gcd(b,a%b);
}

然后是扩展欧几里得,用来求 ax+by=gcd(a,b)ax+by=\mathrm{gcd}(a,b) 的一组可行解,首先令

ax1+by1=gcd(a,b)bx2+(amodb)y2=gcd(b,amodb) \begin{aligned} ax_1+by_1&=\mathrm{gcd}(a,b)\\ bx_2+(a\bmod b)y_2&=\mathrm{gcd}(b,a\bmod{b}) \end{aligned}

根据欧几里得算法,有 gcd(a,b)=gcd(b,amodb)\mathrm{gcd}(a,b)=\mathrm{gcd}(b,a\bmod b),因此:

ax1+by1=bx2+(amodb)y2ax1+by1=bx2+(abab)y2ax1+by1=ay2+b(x2aby2) \begin{aligned} ax_1+by_1&=bx_2+(a\bmod b)y_2\\ ax_1+by_1&=bx_2+(a-b\lfloor\frac{a}{b}\rfloor)y_2\\ ax_1+by_1&=ay_2+b(x_2-\lfloor\frac{a}{b}\rfloor y_2)\\ \end{aligned}

可得:

x1=y2y1=x2aby2 \begin{aligned} x_1&=y_2\\ y_1&=x_2-\lfloor\frac{a}{b}\rfloor y_2 \end{aligned}

通过递归可解:

int exgcd(int a,int b,int &x,int &y){
	if(!b){x=1,y=0;return a;}
	int d=exgcd(b,a%b,x,y);
	int t=x;x=y;y=t-(a/b)*y;
	return d;
}

由此可以解出 x0,y0x_0,y_0 使得

ax0+by0=gcd(a,b) ax_0+by_0=\mathrm{gcd}(a,b)

成立,为了解出 ax+by=cax+by=c 的一个特解,对上式做一些变换,得:

ax0cgcd(a,b)+by0cgcd(a,b)=c a\frac{x_0c}{\mathrm{gcd}(a,b)}+b\frac{y_0c}{\mathrm{gcd}(a,b)}=c

因此可得原方程得一个特解 x1,y1x_1,y_1

x1=x0cgcd(a,b),  y1=y0cgcd(a,b) x_1=\frac{x_0c}{\mathrm{gcd}(a,b)},\;y_1=\frac{y_0c}{\mathrm{gcd}(a,b)}

然后构造原方程通解,令 dQd\in\mathbb{Q},则

a(x1+db)+b(y1da)=c a(x_1+db)+b(y_1-da)=c

并且 da,dbZda,db\in\Z,易知 d=1gcd(a,b)d=\dfrac{1}{\mathrm{gcd}(a,b)},令

dx=bgcd(a,b),  dy=agcd(a,b) d_x=\frac{b}{\mathrm{gcd}(a,b)},\;d_y=\frac{a}{\mathrm{gcd}(a,b)}

则通解为:

x=x1+sdxy=y1sdy \begin{aligned} x&=x_1+sd_x\\ y&=y_1-sd_y \end{aligned}

其中 sZs\in \Z

考虑解出来的 x,yx,y 均为正整数,则

x1+sdx>0    s>x1dxy1sdy>0    s<y1dy \begin{aligned} x_1+sd_x>0&\implies s>-\frac{x_1}{d_x}\\ y_1-sd_y>0&\implies s<\frac{y_1}{d_y} \end{aligned}

因此

L=x1+1dxsy11dy=R L=\lceil\frac{-x_1+1}{d_x}\rceil\le s\le \lfloor\frac{y_1-1}{d_y}\rfloor=R

假如 L>RL>R,则不存在正整数解,s=Ls=L 时得 xminx_{\min}s=Rs=R 时得 xmaxx_{\max}

否则,存在正整数解,解的个数是 RL+1R-L+1s=Ls=L 时可以得到 xmin,ymaxx_{\min},y_{\max}s=Rs=R 时可以得到 xmax,yminx_{\max},y_{\min}

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
int T;
ll a,b,c;
ll exgcd(ll a,ll b,ll &x,ll &y){
    if(!b){x=1,y=0;return a;}
    ll d=exgcd(b,a%b,x,y);
    ll t=x;x=y;y=t-a/b*y;
    return d;
}
int main(){
#ifdef WINE
    freopen("data.in","r",stdin);
#endif
    scanf("%d",&T);
    while(T--){
        ll x0,y0;
        scanf("%lld%lld%lld",&a,&b,&c);
        ll d=exgcd(a,b,x0,y0);
        if(c%d){
            printf("-1\n");
            continue;
        }
        ll x1=x0*c/d,y1=y0*c/d;
        ll dx=b/d,dy=a/d;
        ll L=(ll)ceil((-x1+1)*1.0/dx);
        ll R=(ll)floor((y1-1)*1.0/dy);
        ll xmin=x1+L*dx;
        ll ymin=y1-R*dy;
        if(L>R){
            printf("%lld %lld\n",xmin,ymin);
            continue;
        }
        ll xmax=x1+R*dx,ymax=y1-L*dy;
        printf("%lld %lld %lld %lld %lld\n",R-L+1,xmin,ymin,xmax,ymax);
    }
    return 0;
}

posted @ 2020-06-27 10:09  winechord  阅读(242)  评论(0编辑  收藏  举报