给定一个二元一次方程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);
}