0%

求解二元一次方程

二元一次不定方程(exged)

目录:

题目概要
拓展欧几里得算法(exgcd)
求二元一次方程/线性同余方程特解
求二元一次方程/线性同余方程通解
解决问题

题目概要

给定二元一次不定方程ax+by=c
若该方程无整数解,输出 -1
若该方程有整数解,且有正整数解,则输出其正整数解的数量,所有正整数解中 x 的最小值,所有正整数解中 y 的最小值,所有正整数解中 x 的最大值,以及所有正整数解中 y 的最大值
若方程有整数解,但没有正整数解,你需要输出所有整数解中 x 的最小正整数值, y 的最小正整数值

正整数解即为 x,y 均为正整数的解
整数解即为 x,y 均为整数的解
x 的最小正整数值即所有 x 为正整数的整数解中 x 的最小值,y 同理

数据范围:1<=a,b,c<=1e9 , 输入含有多组数据,数据组数不超过2*10^5

样例输入:
2
2 11 100
3 18 6

样例输出:
4 6 2 39 8
2 1

拓展欧几里得算法

扩展欧几里得算法:用于求解形如ax+by=gcd(a,b)的不定方程特解。
求解方法&证明: 当b=0时,可以看出gcd(a,b)=a,而x=1,y=0是方程的一组特解
当b!=0时,根据欧几里得算法递归求解exgcd(b,a%b,x,y),设a’=b,b’=a%b,可以求得a’x’+b’y’=gcd(a,b)的一组特解,即x’,y’。
而ax+by=gcd(a,b)=a’x’+b’y’,将a’=b,b’=a%b代入,得x=y’,y=x-a/b*y’。

Code

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

求二元一次方程组的特解

二元一次方程: 对于形如ax+by=c的二元一次方程,裴蜀定理指出,当且仅当gcd(a,b)|c(|为整除)时,存在整数解。由裴蜀定理我们可以得到一个推论:a,b互质的充要条件是存在整数x,y使ax+by=1.
设g=gcd(a,b),a’=a/g,b’=b/g,c’=c/g,则ax+by=c⇔a’x+b’y=c’,
此时gcd(a’,b’)=1,可以利用exgcd求出a’x’+b’y’=1的一组特解,继而得出x=c’y’, y=c’x’,从而得到ax+by=c的一组特解

线性同余方程: 对于形如ax\equiv≡c(mod b)的线性同余方程,根据模运算的定义,在方程左侧添加一个by不会对结果造成影响,其实质就等价于ax+by=c的不定方程,利用上述方法求解便可。

求二元一次方程/线性同余方程通解

设g=gcd(a,b), a’=b/g, b’=a/g, x0,y0为方程的一组特解, 则通解为x=x0+a’t, y=y0-b’t(t为整数)

证明
Q:为什么它是通解?
A:因为将该解代入方程恒成立
Q:但还有其他形式的解能满足,例如x=x0+bt, y=y0-at,为什么选他呢?
A:因为他能无遗漏地表示所有正整数解,因为lcm(a,b)=ab/g, x,y的系数分别为a,b,所以x,y的在满足方程的前提下的最小变化量为b/g,a/g,证毕。
线性同余方程同理

解决问题

至此我们具备了解决问题所需要的所有知识
无整数解: 要求输出-1。根据扩展欧几里得算法的定义可得,当c%gcd(a,b)≠0时方程无整数解,输出-1即可。
判断有无正整数解: 根据二元一次方程的通解可得,x,y的增减性相反,所以若存在正整数解,则当x取得最小正整数解时,y取得最大正整数解,若此时y≤0,则说明该方程无正整数解。最小整数解可以通过取余快速得到,如x的最小整数解为(x0%a’+a’)%a’,这里需要特判下,当x=0时另x=a’,y同理。
解组个数: 易得正整数解的组数为(Xmax-Xmin)/a’+1或(Ymax-Ymin)/b’+1.

Code:

#include<cstdio>
#define ll long long    //注意开long long

ll read(){
    char ch;
    ll num=0;
    ch=getchar();
    while(ch<'0'||ch>'9')
        ch=getchar();
    while(ch>='0'&&ch<='9'){
        num=num*10+ch-'0';
        ch=getchar();
    }
    return num;
}

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

int main(){
    ll n,x,y,a,b,c,d;
    n=read();
    while(n--){
        a=read(), b=read(), c=read();
        d=ex_gcd(a,b,x,y);
        if(c%d!=0){
            printf("-1\n");
            continue;
        }
        x*=c/d, y*=c/d;
        ll temp=(x%(b/d)+b/d)%(b/d);    //temp为x的最小正整数解
        if(temp==0)                //特判
            temp=b/d;
        if((c-a*temp)/b<=0){
            int q=(y%(a/d)+a/d)%(a/d);    //q为y的最小正整数解
            if(!q)
                q=a/d;
            printf("%lld %lld\n",temp,q);
            continue;
        }
        ll temp2=(y%(a/d)+a/d)%(a/d);    //temp2为y的最小正整数解
        if(!temp2)
            temp2=a/d;
        int maxx=(c-temp2*b)/a;        //maxx为x的最大正整数解
        printf("%lld %lld %lld %lld %lld\n",(maxx-temp)/(b/d)+1,temp,temp2,maxx,(c-temp*a)/b);
    }
    return 0;
}

此题为洛谷P5656,建议去洛谷切下这道题来巩固,洛谷CF7C Line为弱化版,只用找出一组整数解即可