[经典题目]
:
给出方程ax+by+c=0,求出有多少对整数解(x, y),满足x1≤x≤x2,y1≤y≤y2。
[思路]
:
首先,对方程ax+by+c=0,讨论其特例。
[1] 如果a=0,b=0,并且c≠0,则无解。如果a=0,b=0,并且c=0,则整数根的数目为((x2- x1+1)*( y2- y1+1))。
[2] 如果a=0并且b≠0,则by=c。如果c 不是b的倍数,或c/b不是[y1, y2]中的元素,则无解;否则对在[x1, x2]中的每个x,(x, c/b)是整数根。
[3] 如果b=0并且a≠0,则和[2]相同。
[4] 如果c不是GCD(a, b)的倍数,则无解。
然后,解答过程如下。
[1] 方程式ax+by+c=0 写为ax+by= - c;
[2] 如果a是负数,则a 的值取反;而且如果a 的值取反,则x的值也取反;也就是说,区间[x1, x2]改为[-x2, -x1]。对于b和y,也是一样。(这里a,b,c都变为正值的原因是为了后面好处理数据)
[3] 采用扩展欧几里得算法计算初始解x0和y0。
[4] 计算方程的整数根(x, y) ,x= x0+kb,y= y0-kb,k∈Z。如果x∈[x1, x2]且y∈[y1, y2], (x, y) 是一个整数根。
此外,除法运算中有一个问题,如何将实数转换为整数?对于上界,向下取整;对于下界,向上取整。例如,如果2.5≤k≤5.5,则k 可以是3, 4, 5; 如果-5.5≤k≤-2.5,则k可以是 -3, -4, -5。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int T;
ll a,b,c,x1,x2,yy1,y2;
ll exgcd(ll a,ll b,ll &x,ll &y){
if(b==0){
x=1,y=0;
return a;
}
ll d=exgcd(b,a%b,y,x);
y-=a/b*x;
return d;
}
void change(ll &x,ll &y){
ll t=y;
y=-x;x=-t;
}
int main()
{
scanf("%d",&T);
for(int t=1;t<=T;t++){
scanf("%lld%lld%lld%lld%lld%lld%lld",&a,&b,&c,&x1,&x2,&yy1,&y2);
printf("Case %d: ",t);
c=-c;
if(c<0){a=-a;b=-b;c=-c;}
if(a<0){a=-a;change(x1,x2);}
if(b<0){b=-b;change(yy1,y2);}
if(a==0&&b==0){ // a=0,b=0
if(c==0)printf("%lld\n",(x2-x1+1)*(y2-yy1+1));
else printf("0\n");
}
else if(a==0){ // a=0
if(c%b==0&&c/b>=yy1&&c/b<=y2)printf("%lld\n",x2-x1+1);
else printf("0\n");
}
else if(b==0){ // b=0
if(c%a==0&&c/a>=x1&&c/a<=x2)printf("%lld\n",y2-yy1+1);
else printf("0\n");
}
else {
ll x,y;
ll d=exgcd(a,b,x,y);
if(c%d!=0)printf("0\n");
else {
x*=c/d;y*=c/d; // 一组解(x0,y0)
// 根据公式x=x0+kyb’, y=y0-ka’(其中a’=a/gcd(a,b),b’=b/gcd(a,b)),分别求出对应的k区间
ll kx1=ceil(double(x1-x)/(b/d)),kx2=floor(double(x2-x)/(b/d)); // [x1,x2]对应的k值的范围[kx1,kx2]
ll ky1=ceil(double(y-y2)/(a/d)),ky2=floor(double(y-yy1)/(a/d));// [yy1,y2]对应的k值的范围[ky1,ky2]
//
ll down=kx1>ky1?kx1:ky1;
ll up=kx2<ky2?kx2:ky2;
if(down>up)printf("0\n");
else printf("%lld\n",up-down+1);
}
}
}
return 0;
}