uva11768 - Lattice Point or Not 扩展gcd

解决如何计算带有浮点坐标的线段上整数点的数量问题,通过扩展欧几里得算法找到特定解,并利用最小公倍数确定整数点分布规律。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Now a days a very common problem is:“The coordinate of two points in Cartesian coordinate system is (200, 300) and(4000, 5000). If these two points are connected we get a line segment. How manylattice points are there on this line segment.” You will have to do a similartask in this problem – the only difference is that the terminal coordinates canbe fractions.

 

Input

First line of the input file contains a positive integer N(N<=50000) that denotes how many lines of inputs follow. This line isfollowed by N lines each of which contains four floating-point numbers x1, y1,x2, y2 (0< |x1|, |y1|, |x2|, |y2|<=200000). These floating-point numbers has exactly one digit after thedecimal point.

 

Output

For each line of input exceptthe first line produce one line of output. This line contains an integer whichdenotes how many lattice points are there on the line segment that connects thetwo points (x1, y1) and (x2, y2).

 

SampleInput                              Output for Sample Input

3

10.1 10.1 11.2 11.2

10.2 100.3 300.3 11.1

1.0 1.0 2.0 2.0

1

0

2


  这题好坑。。

  一开始就有想法,先找到离上面端点最近的一个在直线上的整点,然后根据最小公倍数的循环节算出这个点到下端点之间有多少个。

  小数乘以10,问题就变成了先找一个是10的倍数的点,把坐标带入得到方程(Y1-Y2)x+(X2-X1)y=Y1X2-Y2X1,把等号左边乘10变成10(Y1-Y2)x+10(X2-X1)y=Y1X2-Y2X1,用扩展gcd求出x,y,再乘以10,就成了10的倍数的解。这是个好方法~

      后面求循环节就类似于一个矩形对角线上能经过多少个整点的问题,答案是每经过gcd(N,M)有一个。还要注意的是根据解的位置分情况求出离上端点最近的整点,还有各种特判。。呵呵呵。。。

#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<set>
#include<map>
#include<vector>
#include<stack>
#include<algorithm>
#define eps 1e-9
#define MAXN 30010
#define MAXM 110
#define MOD 999983
typedef long long LL;
using namespace std;
int T;
double t1,t2,t3,t4;
LL X1,Y1,X2,Y2;
void extend_gcd(LL a,LL b,LL& d,LL& x,LL &y){
    if(!b){
        d=a;
        x=1;
        y=0;
    }
    else{
        extend_gcd(b,a%b,d,y,x);
        y-=a/b*x;
    }
}
LL gcd(LL a,LL b){
    return a%b?gcd(b,a%b):b;
}
LL lcm(LL a,LL b){
    return a*b/gcd(a,b);
}
int get_one_reult(LL &x,LL &y){
    LL d;
    extend_gcd(10*(Y1-Y2),10*(X2-X1),d,x,y);
    if((Y1*X2-Y2*X1)%d!=0) return 0;
    x=10*x*(Y1*X2-Y2*X1)/d;
    y=10*y*(Y1*X2-Y2*X1)/d;
    return 1;
}
int main(){
    freopen("in.txt","r",stdin);
    scanf("%d",&T);
    while(T--){
        scanf("%lf%lf%lf%lf",&t1,&t2,&t3,&t4);
        if(t1>=0) X1=LL(t1*10+0.5);
        else X1=LL(t1*10-0.5);
        if(t2>=0) Y1=LL(t2*10+0.5);
        else Y1=LL(t2*10-0.5);
        if(t3>=0) X2=LL(t3*10+0.5);
        else X2=LL(t3*10-0.5);
        if(t4>=0) Y2=LL(t4*10+0.5);
        else Y2=LL(t4*10-0.5);
        if(Y1>Y2){
            swap(X1,X2);
            swap(Y1,Y2);
        }
        if(X1==X2&&Y1==Y2){
            if(X1%10||Y1%10) printf("0\n");
            else printf("1\n");
            continue;
        }
        LL N=abs(X1-X2),M=abs(Y1-Y2),x,y,ans,ymin,xmin;
        if(M==0){
            if(Y1%10){
                printf("0\n");
                continue;
            }
            if(X1>X2) swap(X1,X2);
            if(X1%10) xmin=X1+10-X1%10;
            else xmin=X1;
            if(xmin<=X2) ans=1+(X2-xmin)/10;
            else ans=0;
            printf("%lld\n",ans);
            continue;
        }
        if(N==0){
            if(X1%10){
                printf("0\n");
                continue;
            }
            if(Y1%10) ymin=Y1+10-Y1%10;
            else ymin=Y1;
            if(ymin<=Y2) ans=1+(Y2-ymin)/10;
            else ans=0;
            printf("%lld\n",ans);
            continue;
        }
        if(!get_one_reult(x,y)){
            printf("0\n");
            continue;
        }
        LL d=gcd(N,M);
        LL a=10/gcd(N/d,10),b=10/gcd(M/d,10),n=lcm(a,b),cirx=N/d*n,ciry=M/d*n;
        if(y>Y1) ymin=Y1+(y-Y1)%ciry;
        else{
            if((Y1-y)%ciry) ymin=Y1+ciry-(Y1-y)%ciry;
            else ymin=Y1;
        }
        xmin=(Y1*X2-Y2*X1+(X1-X2)*ymin)/(Y1-Y2);
        if(X1<=X2){
            if(xmin>=X1&&xmin<=X2) ans=1+(X2-xmin)/cirx;
            else ans=0;
        }
        else{
            if(xmin>=X2&&xmin<=X1) ans=1+(xmin-X2)/cirx;
            else ans=0;
        }
        printf("%lld\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值