lightoj 1077 How Many Points? gcd

缘起

日常水题 lightoj 1077 How Many Points?

分析

题意很简单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
平面上给定两个整数格点A和B. 求出连接A和B的线段上有几个格点? 注意,包括A和B

【输入】
多样例,第一行是样例数T(<=125), 每个样例四个整数Ax, Ay, Bx and By. Each of them will be fit into a 32 bit signed integer.

【输出】
对每个样例输出AB线段上的格点数目.

【样例输入】
2
3 3 -1 -1
0 0 5 2

【样例输出】
Case 1: 5
Case 2: 2

【限制】
Time limit 500 ms
Memory limit 32768 kB

【来源】
Problem Setter: Jane Alam Jan

因为坐标范围到达了int,所以暴力遍历是不合适的. 合理的算法是gcd. 考虑一个格点(x,y)在[A,B]线段上(假设xA<xB),那么
$$
(x_B-x_A)/(y_B-y_A) = (x-x_A)/(y-y_A), \ x \in [x_A, x_B]
$$
而左边可以约分, 首先使用欧几里得算法求出分子分母的最大公约数,然后约分得到a1/b1的形式,其中(a1,b1)=1

然后(x,y)的通解就是形如
$$
\begin{cases}
x = x_A+a_1t \
y = y_A+b1
t
\end{cases}
$$
其中, t>=0.

ac代码如下(题目虽然简单,但是坑不少)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//#include "stdafx.h"

#include <stdio.h>
//#define LOCAL
#include <algorithm>
using namespace std;
typedef long long LL; // 注意 -2000000000 -2000000000 2000000000 2000000000 这样的数据

LL x1,y11,x2,y2; //(x1,y1)是A的坐标, (x2,y2)是B的坐标

LL gcd(LL a, LL b) // 欧几里得
{
LL t;
while(b)
{
t = a;
a = b;
b = t%b;
}
return a;
}

LL sz()
{
if (x1>x2)
{
swap(x1,x2);
swap(y11, y2);
} // 确保 x1<=x2
if (x1==x2)
{
return y2>y11?y2-y11+1:y11-y2+1;
} // 下面的代码保证 x1<x2
if (y11==y2)
{
return x2-x1+1;
} // 下面的代码保证 x1<x2 && y11!=y2
LL d = gcd(x2-x1, y2-y11); // d可能为负值
if (d<0) d = -d; // 保证d为非负
LL a1 = (x2-x1)/d, b1 = (y2-y11)/d; // 则a1>0
return (x2-x1)/a1+1; // x1+t*a1<=x2, 得出t的个数
}

int main()
{
#ifdef LOCAL
freopen("d:\\data.in", "r", stdin);
//freopen("d:\\my.out", "w", stdout);
#endif
int t;
scanf("%d", &t);
for (int i = 1; i<=t; i++)
{
scanf("%lld%lld%lld%lld", &x1, &y11, &x2, &y2);
printf("Case %d: %lld\n", i, sz());
}
return 0;
}

ac情况

Status Accepted
Memory 1156kB
Length 962
Lang C++
Submitted 2019-08-23 09:43:47
Shared
RemoteRunId 1561956