zoukankan      html  css  js  c++  java
  • 【luoguP5656】二元一次不定方程(gcd,exgcd,裴蜀定理,不定方程初步)

    我是题目链接qwq

    前言(概述)

    这个题目对于像我这种的数论新手极其不友好,首先这个题面就很不友好,所以我们先来分解一下题面,看看他想让我们干什么qwq。

    首先,我们需要判断给定的不定方程有无整数解,若无,输出-1,若有,则分类讨论:

    • 有正整数解,则先输出解的数量,再按顺序输出x的最小正整数解,y的最小正整数解,x的最大正整数解,y的最大正整数解。
    • 无正整数解,则输出x的最小正整数解,y的最小正整数解。

    现在我们知道了要求什么了,那么怎么求呢qwq,让我们一步一步来。

    背景芝士

    裴蜀定理

    要解决这个问题我们首先要明白一个叫做裴蜀定理的东西(左转),即:

    对于任何整数$a$,$b$和它们的最大公约数$d$,关于未知数$x$和$y$的线性不定方程(称为裴蜀等式):

    若$a$,$b$是整数,且$gcd(a,b)=d$,那么对于任意的整数$x$,$y$,$ax+by$都一定是$d$的倍数。

    特别地,一定存在整数$x$,$y$使$ax+by=d$成立。——百度百科。

    简单来说,就是不定方程$ax+by=c$,其中$xinmathbb{N}^+$,$yinmathbb{N}^+$,有解的充要条件是$gcd(a,b)|c$

    那么下面我们来证明一下:

    首先,设$s=gcd(a,b)$,

    显然$s|a$,$s|b$,

    $ecause x,yinmathbb{N}^+$,

    $ herefore s|ax,s|by,s|ax+by$,

    令$p$为$ax+by$的最小正整数值,

    $q=lfloor frac{a}{p} floor$,

    $r=a mod p=a-qp=a-q(ax+by)=a(1-qx)+bqy$,

    可知,$r$也是$a$,$b$的线性组合(简单来说就是$r$也属于$ax+by$的一种形式)。

    $ecause p$为$ax+by$的最小正整数值,$0 le r < p$,

    故$r=0$,即$p|a$。

    同理,可得$p|b$,

    $ecause$所有的$ax+by$都是$s$的倍数。

    $ herefore p=d$。

    $mathrm{Q.E.D.}$

    欧几里得定理求gcd

    P.S.如果会求gcd可以跳过qwq

    那么如何求两个数的gcd呢,一般有三种方法:

    1. 欧几里得(辗转相除)法
    2. 九章算术·更相减损法
    3. stein算法
    4. 暴力

    我们这里只讲最常用的欧几里得算法

    即:$gcd(a,b)=gcd(b,a mod b)$

    证明:

    设$a=kb+r$

    则有,$r=a mod b$

    设:$d$为$a$,$b$的一个任意的公约数,

    则有$a equiv b equiv 0 pmod{d}$

    则,由同余的性质可得:

    $a-kb equiv r equiv 0 pmod{d}$

    $ herefore d$是$b$和$a mod b$的公约数。

    $ecause forall d|gcd(a,b)$都满足此性质

    $ herefore gcd(a,b)=gcd(b,a mod b)$

    $mathrm{Q.E.D.}$

    然后我们就可以得到一个很简单的函数:

    int gcd(int a,int b)
    {
        return b?gcd(b,a%b):a;
    }

    时间复杂度$O(logn)$,大部分情况下已经够用了,如果你想要更快,更强,那么你可以尝试了解一下stein算法,他是对欧几里得算法的一种优化,比这个要快。

    exgcd解线性同余方程

    有欧几里得,就一定会有拓展欧几里得。——鲁迅

    咳咳,下面我们来说拓展欧几里得算法qwq。

    欧几里得算法提供了一种快速计算最大公约数的方法。而扩展欧几里得算法不仅能够求出其最大公约数。而且能够求出a,b和其最大公约数构成的不定方程ax+by=c的两个整数x,y(这里x和y不一定为正数)。

    那么,拓欧(是拓展欧几里得,不是那个毒瘤的拓展欧拉定理,究竟是怎么运算的呢,让我们来康康

    首先我们先求我们先求对于$ax+by=1$且$a perp b$的一组整数解$(x,y)$(为了避免我口胡,下面说的所有解都是整数解qwq

    (by the way,$ax+by=c$与$ax equiv c pmod{b}$等价,且由裴蜀定理可知$ax+by=1$有一组整数解为$a perp b$的充要条件。

    那么下面让我们用朴素欧几里得算法来推一推式子,来求出上式的一组整数解:

    $ax+by=1$

    $ecause a perp b$

    $ herefore ax+by=gcd(a,b)$

    $ecause gcd(a,b)=gcd(b,a mod b)$

    $ herefore ax+by=gcd(b,a mod b) ightarrow bx+(a mod b)y$

    $ecause a mod b=a- lfloor frac{a}{b} floor b$

    $ herefore ax+by=bx+(a- lfloor frac{a}{b} floor b)y$

    提取公因数,得:

    $ax+by=ay+b(x- lfloor frac{a}{b} floor y)$

    此时,我们可以发现,不定方程的一组解为$egin{cases} x=y\y=(x- lfloor frac{a}{b} floor y) end{cases}$

    于是乎我们就求出了这个方程的一组解,那么这个式子有什么用呢,我们要解的方程$ax+by=c$与它有什么关系呢?

    我们发现,对于任意的$ax+by=c$满足$gcd(a,b)|c$都可以转化为$ax+by=1$的形式,然后我们就可以使用exgcd进行求解了(由裴蜀定理可知,若$gcd(a,b) mid c$则该方程无解。

    下面让我们对这个更一般的方程$ax+by=c$转化为$ax+by=1$的形式:

    令$a'= frac{a}{gcd(a,b)} quad b'= frac{b}{gcd(a,b)} quad c'= frac{c}{gcd(a,b)}$

    $ herefore a'x+b'y=c'$

    $ herefore a' frac{x}{c'}+b' frac{y}{c'}=1$

    令$x'=frac{x}{c'} quad y'=frac{y}{c'}$

    $ herefore a'x'+b'y'=1$

    然后我们使用刚才的方法跑一遍exgcd即可得到此方程的一组解$(x',y')$

    则原方程的解$egin{cases} x=x'c'\y=y'c' end{cases}$

    此时,我们就可以通过递归求出这组解

    参考代码如下:

    void exgcd(int a,int b,int &x,int &y)
    {
        if(!b)
        x=1,y=0;
        else
        {
            exgcd(b,a%b,x,y);
            int t=x;
            x=y;
            y=t-a/b*y;
        }
    }
    /*可以将函数定为int类型,在else处定义变量gcd=exgcd(b,a%b,x,y),在else的最后return gcd;在if处最后return a,此时的gcd或a即为gcd(a,b)。
    函数参数中a,b,x,y即为方程中的a,b,x,y,初始时,x,y不赋值,因为是传址调用,函数递归结束后的x,y即为原方程的解。*/

    解系扩展 

    通过上述方法我们只求出了方程的一组解,而且这个解是随机的一组解,它可能并不是我们想要的解(如本题,很多题让我们求出$x or y$的最小正整数解。

    这时我们就需要讲单个解$(x,y)$扩展成一个解系(解集。

    构造一个$p,q$,即我们想要让$x$变为$x+p$,让$y$变成$y-q$

    $egin{cases} a(x_0+p)+b(y_0-q)=c\ax_0+by_0=c end{cases}$

    解方程组,可知,$p=frac{bq}{a}$

    同时,因为我们要保证$a,b,x,y$都为整数,

    所以$a|bq,b|bq$

    即最小的$bq=lcm(a,b)=frac{ab}{gcd(a,b)}$

    所以最小的$p=frac{b}{gcd(a,b)},q=frac{a}{gcd(a,b)}$

    返回刚才的等式$a(x_0+p)+b(y_0-q)=c$,我们就得到了这里最小的$p$和$q$

    这时我们只需用一个整数系数$t$乘上$p$和$q$就可以得到整个解系了。

    到这里,我们就有了可以解出这个题的基础芝士,下面,我们来对这个问题逐步求解。

    逐步求解

    有无整数解

    根据裴蜀定理我们可知,第一个问题,有无正整数解,即可转化为判断$gcd(a,b)|c   or   gcd(a,b) mid c$。

    当$gcd(a,b)|c$时,有解;

    反之,无解。

    第一个问题解决。

    x或y的最小值

    刚才我们对方程进行了解析拓展,得到了所有的$(x,y)$

    我们回到刚才的等式,变换一下变量,

    那么我们就有了一组通解,

    注意0不为正整数,所以需要特判(这个会卡掉几个点。。。)

    若$x or y=0$则将其加上$b or a$即可

    第二个问题解决。

    x或y的最大值

    由$a'x+b'y=c'$可知,$x$与$y$的数量大小成反比

    $ herefore$ egin{cases} a'x_{min}+b'y_{max}=c'\a'x_{max}+b'y_{min}=c' end{cases} egin{cases} x_{max}=frac{c'-b'y_{min}}{a'}\y_{max}=frac{c'-a'x_{min}}{b'} end{cases}

    第三个问题解决。

    判断有无正整数解

    有了上面的$x_{max} and y_{max}$的值,我们就可以判断,若最大值还不为正整数,则无正整数解

    第四个问题解决。

    正整数解的数量

    由$x=x_{min}+b'k$可知,$x_{min}$到$x_{max}$之间每有$b'$个数,就有一个解。

    所以正整数解的数量即为$lfloor frac{x_{max}-x_{min}}{b'}+1 floor$(+1是因为边界问题,向下取整会舍去,所以会少算一个边界

    y同理,如果你喜欢y你也可以用y求qwq。

    第五个问题解决,然后这个毒瘤的题目就结束惹qwq。

    参考代码

    #include<bits/stdc++.h>
    
    using namespace std;
    
    typedef long long ll;
    
    inline long long read()
    {
        ll x=0;
        char ch=getchar();
        while(ch<'0'||ch>'9')
        {
            ch=getchar();
        }
        while(ch>='0'&&ch<='9')
        {
            x=(x<<1)+(x<<3)+(ch^48);
            ch=getchar();
        }
        return x;
    }
    
    inline void print(long long x)
    {
        if(x>9) 
        print(x/10);
        putchar(x%10+'0');
    }
    
    ll mul(ll a,ll b,ll p)
    {
        return a*b%p;
    }
    
    ll Gcd(ll a,ll b)//stein算法求gcd
    {
        ll c=0;
        while(1)
        {
            if(!a)
            return b<<c;
            else if(!b)
            return a<<c;
            if(!(a&1)&&!(b&1))
            a>>=1,b>>=1,++c;
            else if(!(a&1)&&(b&1))
            a>>=1;
            else if((a&1)&&!(b&1))
            b>>=1;
            else if((a&1)&&(b&1))
            {
                ll x=min(a,b);
                a=abs(a-b);
                b=x;
            }
        }
    }
    
    void exgcd(ll a,ll b,ll &x,ll &y)
    {
        if(!b)
        {
            x=1ll,y=0ll;
        }
        else
        {
            exgcd(b,a%b,x,y);
            ll t=x;
            x=y;
            y=t-a/b*y;
        }
    }
    
    ll t;
    
    int main()
    {
        t=read();
        while(t--)
        {
            ll a=read(),b=read(),c=read(),x,y;
            ll gcd=Gcd(a,b);
            if(c%gcd)
            {
                puts("-1");
                continue;
            }
            else
            {
                a/=gcd,b/=gcd,c/=gcd;
                exgcd(a,b,x,y);
                x*=c,y*=c;
                ll xmin=(x%b+b)%b;
                if(!xmin)
                xmin+=b;
                ll ymin=(y%a+a)%a;
                if(!ymin)
                ymin+=a;
                ll xmax=(c-b*ymin)/a;
                ll ymax=(c-a*xmin)/b;
                if(xmax<=0||ymax<=0)
                {
                    print(xmin);
                    printf(" ");
                    print(ymin);
                    puts("");//由于他这道毒瘤题卡常过于严重,所以这个鬼畜的输出请见谅qwq
                }
                else
                {
                    ll sum=(xmax-xmin)/b+1;
                    print(sum);
                    printf(" ");
                    print(xmin);
                    printf(" ");
                    print(ymin);
                    printf(" ");
                    print(xmax);
                    printf(" ");
                    print(ymax);
                    puts("");
                }
            }
        }
    }
    对不起啊,因为我是一个活在二次元的人,生为野犬,喻世,勿争了吧。
  • 相关阅读:
    NT头 IMAGE_NT_HEADER
    组合框
    列表框消息大全
    滚动条
    列表框
    超级列表框
    按钮
    EDIT编辑框
    15. 三数之和
    268. 缺失数字
  • 原文地址:https://www.cnblogs.com/YLCHANGE/p/13523248.html
Copyright © 2011-2022 走看看