SGU106 The equation(数论)

给定一个二元一次方程ax+by+c=0,求该方程在x1<=x<=x2,y1<=y<=y2内有多少组解。

扩展欧几里得的应用。


先移项,c=-c,得到ax+by=c

特判一下a,b是否为0(貌似这道题的数据不用特判也没有关系)

令d=gcd(a,b),然后把a,b当做正数用扩展欧几里得ax+by=d的一组解x0,y0;如果原来的a是<0,我们把x0的值取反。

显然,如果c%d不等于0的话,肯定无解

然后x0=x0*c/d,y0=y0*c/d得到方程ax+by=c的一组解

令△x=|b/d|,△y=|a/d|,那么x的通解为x=x0+△x*t(t为整数)

之后根据通解得到x1<=x<=x2内的x的最大最小解lx,rx

由ax+by=c得到y=(c-ax)/b,因此由lx,rx得到对应的ly,ry由于方程代表的直线ax+by=c斜率可能为负,x与y可能反相关,所以交换ly,ry的顺序保证ry>ly

再重复上述求lx,rx的方法,订正ly,rx,确保ly,ry在范围y1<=y<=y2内。

最后,方程的解数就是(ry-ly)/△y+1

#include<cstdio>
#include<iostream>
using namespace std;
typedef long long LL;
inline LL abs(LL x) {return x>0?x:-x;}
void exgcd(LL a,LL b,LL &d,LL &x,LL &y)
{
	if(b == 0){x = 1;y = 0;d = a;}
	else
	{
		exgcd(b,a%b,d,y,x);
		y -= x*(a/b);
	}
}
LL a,b,c,x1,x2,y1,y2;
int main()
{
	cin>>a>>b>>c;
	cin>>x1>>x2>>y1>>y2;
	c = -c;
	if(x1 > x2||y1 > y2)
	{
		printf("0\n");
		return 0;
	}
	if(a == 0&&b == 0)
	{
		if(c == 0) printf("%I64d\n",(x2-x1+1)*(y2-y1+1));
		else printf("0\n");
		return 0;
	}
	else if(a == 0)
	{
		if(c%b == 0&&c/b >= y1&&c/b <= y2) printf("%I64d\n",x2-x1+1);
		else printf("0\n");
		return 0;
	}
	else if(b == 0)
	{
		if(c%a == 0&&c/a >= x1&&c/a <= x2) printf("%I64d\n",y2-y1+1);
		else printf("0\n");
		return 0;
	}
	LL x0,y0,d,delta_x,delta_y,delta,lx,rx,ly,ry;
	exgcd(abs(a),abs(b),d,x0,y0);//如果原来的a<0,那么x也要取反
	if(a < 0) x0 = -x0;
	if(b < 0) y0 = -y0;
	if(c%d)
	{
		printf("0\n");
		return 0;
	}
	x0 *= c/d;//通过Δx求出通解x = x0 + k*Δx
	y0 *= c/d;
	delta_x = abs(b/d);
	delta_y = abs(a/d);
	delta = (x0%delta_x - x1%delta_x + 10*delta_x)%delta_x;
	lx = x1 + delta;//x的最小解
	delta = (x0%delta_x - x2%delta_x + 10*delta_x)%delta_x;
	rx = x2 + delta;
	if(delta != 0) rx -= delta_x;//不能超过rx
	if(rx < lx)
	{
		printf("0\n");
		return 0;
	}
	ly = (c - a*lx)/b;
	ry = (c - a*rx)/b;
	if(ly > ry) swap(ly,ry);
		//如果方程代表的斜率为负,那么x与y反相关,会出现,ly>ry的情况
		//而我们又不用知道x与y的比例关系,只需要根据x的取值范围求出y的取值范围就行了
	if(ly < y1)//如果通过x求出的ymin比y的规定最小值小
	{
		delta = (y0%delta_y - y1%delta_y + 10*delta_y)%delta_y;
		ly = y1 + delta;
	}
	if(ry > y2)
	{
		delta = (y0%delta_y - y2%delta_y + 10*delta_y)%delta_y;
		ry = y2 + delta;
		if(delta != 0) ry -= delta_y;
	}
	if(ry < ly)
	{
		printf("0\n");
		return 0;
	}
	printf("%I64d\n",(ry - ly)/delta_y + 1);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值