题目分析
本题要求计算连接两个给定点的线段上格点(整数坐标点)的数量。与经典格点问题不同的是,本题的输入坐标可能是含一位小数的浮点数,这给问题带来了一定复杂性。
关键理解
- 格点定义:在笛卡尔坐标系中,坐标 ( x , y ) (x, y) (x,y) 均为整数的点。
- 线段上的格点:位于线段上(包括两个端点)且坐标为整数的点。
- 输入特殊性:坐标值范围 ∣ x ∣ , ∣ y ∣ ≤ 200000 |x|, |y| \leq 200000 ∣x∣,∣y∣≤200000,且小数点后恰好有一位数字。
解题思路分析
第一步:消除小数影响
由于输入坐标只有一位小数,我们可以将所有坐标乘以 10 10 10,转化为整数坐标进行处理。例如:
- 原始坐标 ( 10.1 , 10.1 ) (10.1, 10.1) (10.1,10.1) → 转换后 ( 101 , 101 ) (101, 101) (101,101)
- 原始坐标 ( 11.2 , 11.2 ) (11.2, 11.2) (11.2,11.2) → 转换后 ( 112 , 112 ) (112, 112) (112,112)
这样做的目的是避免浮点数精度问题,将所有计算都放在整数域进行。
第二步:线段参数化表示
设转换后的整数坐标为:
- P 1 = ( X 1 , Y 1 ) P_1 = (X_1, Y_1) P1=(X1,Y1)
- P 2 = ( X 2 , Y 2 ) P_2 = (X_2, Y_2) P2=(X2,Y2)
线段上的点可以表示为:
P
(
t
)
=
(
X
1
+
t
⋅
d
x
,
Y
1
+
t
⋅
d
y
)
P(t) = (X_1 + t \cdot dx,\ Y_1 + t \cdot dy)
P(t)=(X1+t⋅dx, Y1+t⋅dy)
其中:
- d x = X 2 − X 1 dx = X_2 - X_1 dx=X2−X1
- d y = Y 2 − Y 1 dy = Y_2 - Y_1 dy=Y2−Y1
- t t t 为参数,且 0 ≤ t ≤ g 0 \leq t \leq g 0≤t≤g,其中 g = gcd ( ∣ d x ∣ , ∣ d y ∣ ) g = \gcd(|dx|, |dy|) g=gcd(∣dx∣,∣dy∣)
这里 g + 1 g+1 g+1 表示线段上等分点的数量(在放大10倍的坐标系中)。
第三步:筛选真正格点
虽然线段上有 g + 1 g+1 g+1 个等分点,但只有那些坐标能被 10 10 10 整除的点才是原坐标系中的整数格点。因为:
- 放大10倍后坐标为 ( X , Y ) (X, Y) (X,Y)
- 原坐标为 ( x , y ) = ( X / 10 , Y / 10 ) (x, y) = (X/10, Y/10) (x,y)=(X/10,Y/10)
- 只有当 X % 10 = 0 X \% 10 = 0 X%10=0 且 Y % 10 = 0 Y \% 10 = 0 Y%10=0 时, ( x , y ) (x, y) (x,y) 才是整数坐标
因此,算法流程为:
- 遍历所有等分点 t = 0 , 1 , 2 , … , g t = 0, 1, 2, \dots, g t=0,1,2,…,g
- 对每个点计算坐标 ( X , Y ) (X, Y) (X,Y)
- 检查是否满足 X % 10 = 0 X \% 10 = 0 X%10=0 且 Y % 10 = 0 Y \% 10 = 0 Y%10=0
- 统计满足条件的点数
第四步:特殊处理
- 两点重合情况:如果 d x = 0 dx = 0 dx=0 且 d y = 0 dy = 0 dy=0,则线段退化为一个点。此时只需检查该点是否为整数格点。
- 坐标相等情况:如果 d x = 0 dx = 0 dx=0 或 d y = 0 dy = 0 dy=0,算法依然适用,因为 gcd \gcd gcd 函数能正确处理。
算法复杂度
- 每组数据需要计算一次 gcd \gcd gcd,时间复杂度 O ( log ( max ( ∣ d x ∣ , ∣ d y ∣ ) ) ) O(\log(\max(|dx|, |dy|))) O(log(max(∣dx∣,∣dy∣)))
- 需要遍历 g + 1 g+1 g+1 个点进行检查,最坏情况下 g g g 可能达到 4 × 1 0 6 4 \times 10^6 4×106(当 ∣ d x ∣ = ∣ d y ∣ = 200000 × 10 |dx|=|dy|=200000 \times 10 ∣dx∣=∣dy∣=200000×10 且互质时)
- 但实际测试中 g g g 通常较小,算法能在时限内通过
数学原理
设
d
x
dx
dx 和
d
y
dy
dy 的最大公约数为
g
g
g,则线段上整数点(在放大
10
10
10 倍的坐标系中)的坐标为:
(
X
1
+
d
x
g
⋅
t
,
Y
1
+
d
y
g
⋅
t
)
,
t
=
0
,
1
,
2
,
…
,
g
\left(X_1 + \frac{dx}{g} \cdot t,\ Y_1 + \frac{dy}{g} \cdot t\right),\ t = 0, 1, 2, \dots, g
(X1+gdx⋅t, Y1+gdy⋅t), t=0,1,2,…,g
这些点均匀分布在线段上,相邻点之间的 x x x 坐标差为 d x g \frac{dx}{g} gdx, y y y 坐标差为 d y g \frac{dy}{g} gdy。
代码实现
// Lattice Point or Not
// UVa ID: 11768
// Verdict: Accepted
// Submission Date: 2025-12-03
// UVa Run Time: 0.110s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
double x1, y1, x2, y2;
scanf("%lf %lf %lf %lf", &x1, &y1, &x2, &y2);
// 乘以10转换为整数坐标,避免浮点误差
int X1 = round(x1 * 10);
int Y1 = round(y1 * 10);
int X2 = round(x2 * 10);
int Y2 = round(y2 * 10);
int dx = X2 - X1;
int dy = Y2 - Y1;
// 处理两点重合的特殊情况
if (dx == 0 && dy == 0) {
if (X1 % 10 == 0 && Y1 % 10 == 0) printf("1\n");
else printf("0\n");
continue;
}
// 计算dx和dy的最大公约数
int g = __gcd(abs(dx), abs(dy));
dx /= g; // x方向每步增量
dy /= g; // y方向每步增量
int count = 0; // 统计整数格点数量
for (int t = 0; t <= g; t++) {
int x = X1 + t * dx;
int y = Y1 + t * dy;
// 检查是否为原坐标系中的整数点
if (x % 10 == 0 && y % 10 == 0) count++;
}
printf("%d\n", count);
}
return 0;
}
测试样例验证
样例输入
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.1 , 10.1 ) (10.1, 10.1) (10.1,10.1) 到 ( 11.2 , 11.2 ) (11.2, 11.2) (11.2,11.2)
- 斜率接近1,线段上有且仅有一个整数格点 ( 11 , 11 ) (11, 11) (11,11)
-
第二组: ( 10.2 , 100.3 ) (10.2, 100.3) (10.2,100.3) 到 ( 300.3 , 11.1 ) (300.3, 11.1) (300.3,11.1)
- 线段上没有任何整数格点
-
第三组: ( 1.0 , 1.0 ) (1.0, 1.0) (1.0,1.0) 到 ( 2.0 , 2.0 ) (2.0, 2.0) (2.0,2.0)
- 端点本身就是整数格点,且线段上只有这两个整数格点
总结
本题的关键在于:
- 坐标转换:通过乘以 10 10 10 将一位小数坐标转换为整数坐标
- 参数化表示:利用最大公约数将线段等分,得到所有候选点
- 条件筛选:检查坐标是否能被 10 10 10 整除,确定是否为真正的整数格点
这种方法避免了浮点数比较的精度问题,将问题完全转化为整数计算,保证了算法的正确性和效率。
234

被折叠的 条评论
为什么被折叠?



