Light oj-1306 Solutions to an Equation

[经典题目]
给出方程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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值