【裴蜀定理】牛牛的方程式

牛牛的方程式

水题,但是没做对

题目描述

牛牛最近对三元一次方程非常感兴趣。众所周知,三元一次方程至少需要三个方
程组成一个方程组,才有可能得出一组解。
牛牛现在想要知道对于方程ax + by + cz = d中有没有至少存在一组{x, y, z}的解,
且x, y, z都为整数,使得方程式成立。

输入格式

第一行输入一个正整数 T,表示测试点中测试样例的组数。
接下来T行,每行四个整数a, b, c, d表示方程ax + by + cz = d中的a, b, c, d

输出格式

如果至少存在一组x, y, z能够满足方程式等式成立,且x, y, z均为整数,请输出
“YES”,否则请输出"NO"。

样例输入1

2
3 1 2 0
2 8 8 3

样例输出1

YES
NO

样例1解释

1*3+(-1)*1+(-1)*2=0
得到一组 x,y,z 的解为{1,-1,-1}为整数使得等式成立,所以输出"YES"。
不存在 x,y,z 为整数使得方程 2x+8y+8z=3 成立,所以输出"NO"。

数据范围

对于10%的测试数据,保证T = 1,−10  ≤ 𝑎, b, c  ≤ 10
对于30%的测试数据,保证−100  ≤ 𝑎, b, c ≤ 100
对于100%的测试数据,保证−10^18 ≤ a, b, c ≤ 10^18
,1 ≤ 𝑇 ≤ 100

思路

看到方程,自然会想到exgcd求二元一次不定方程,或者是裴蜀定理:

ax+by=gxd(a,b)*k=m

GCD

先来看看gcd,这里我用辗转相除法来做的,我以前在网上看到过很多证明,但是都没看懂,这里给出我自己的解释(不知道有没有人和我一样),希望这能够帮到你(讲的真的很详细,认真看完哦)

gcd是用来求最大公约数的。举个例子,我们先求54和24的最大公约数,假设我们已经知道了这两个数的最大公约数是6,那么我们可以把54变为6*9(系数乘上最小公约数),24也能够变为6*4(也是系数乘上最小公约数),我们将较大的数减去较小的数(54-24),直到不能减为止(54-24变为30-24再变为6-24)。因为开始的时候54和24都是最小公约数的倍数,所以这时候剩下的6也一定是最小公约数的倍数。我们继续减,当然,我们将24和6位置互换一下,继续用24减去6,我们发现,减去4次6以后,24变为了0。这时候我们就得到了最小公倍数6。

我们来看一下这个思路能不能化简一下,我们一直减这个操作可以化简为取余的操作。
再放一个程序

int gcd(int a,int b){
	if(a==1||b==0){
		return a;
	}
	if(a<b){
		swap(a,b);
	}
	return gcd(b,a%b);
}

如果这两个数互质,那么他们会一直取余,直到其中一个数变为1,这时候返回1就可以了
最后再用代数来表示一下
将数x和y表示为a*d和b*d(d是最小公约数),x%y=(a-b)* d,直到a-b等于1

裴蜀定理

再让我们来看看裴蜀定理是什么:

对于给定的两个数a和b,一定有两个数(x,y)满足ax+by=gcd(a,b)
证明:
我们还是将a和b表示为p*d和q*d,那么原式就为p*x*d+q*y*d=gcd(a,b)*k
就可以看为(p*x+q*y)=k,且一定有k可以成立
我们这就证明了裴蜀定理

回到题目

三个数?我们就将它简化,将裴蜀定理带入得

gcd(a,b)*k1+c*z=d
gcd(a,b,c)*k2=d

那么我们只要判断d能否整除这三个数的最大公约数

if(a!=0&&b!=0&&c!=0&&d!=0){
	if(d%gcd(gcd(a,b),c)==0){
		cout<<"YES"<<endl;
		continue;
	}
}

这就是核心代码,剩下的,就是一堆特判,因为再gcd中,不能判断0和其他数的最小公约数

代码

#include<bits/stdc++.h>
using namespace std;
long long T,a,b,c,d;
long long gcd(long long a,long long b){
	if(a==1||b==0){
		return a;
	}
	if(a<b){
		swap(a,b);
	}
	return gcd(b,a%b);
}
int main(){
	freopen("niuniufx.in","r",stdin);
	freopen("niuniufx.out","w",stdout);
	scanf("%lld",&T);
	for(int i=1;i<=T;i++){
		scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
        a=abs(a);
        b=abs(b);
        c=abs(c);
        d=abs(d);
		if(a!=0&&b!=0&&c!=0&&d!=0){
			if(d%gcd(gcd(a,b),c)==0){
				cout<<"YES"<<endl;
				continue;
			}
		}
		if(d==0){
			cout<<"YES"<<endl;
			continue;
		}
        if(a==0&&b==0&&c==0){
            cout<<"NO"<<endl;
            continue;
        }
		if(a==0&&b==0&&d%c==0){
			cout<<"YES"<<endl;
			continue;
		}
		if(a==0&&c==0&&d%b==0){
			cout<<"YES"<<endl;
			continue;
		}
		if(c==0&&b==0&&d%a==0){
			cout<<"YES"<<endl;
			continue;
		}
		if(a==0&&d%gcd(b,c)==0){
			cout<<"YES"<<endl;
			continue;
		}
		if(b==0&&d%gcd(a,c)==0){
			cout<<"YES"<<endl;
			continue;
		}
		if(c==0&&d%gcd(b,a)==0){
			cout<<"YES"<<endl;
			continue;
		}
		cout<<"NO"<<endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值