0%

牛客 15087 - 三角形(Pick 定理 + 格点数)

题目大意

给定三角形三个顶点 A(Xa,Ya)A(X_a,Y_a)B(Xb,Yb)B(X_b,Y_b)C(Xc,Yc)C(X_c,Y_c)整数坐标,求三件事:

  • 三角形面积(保留 1 位小数)

  • 三角形内部的整点个数

  • ABABBCBCACAC 上的整点个数(都不含三角形顶点)

输入输出与数据范围

多组输入,每组三行各两个整数(依次是 A、B、C 的坐标),读到 -1 结束。0X,Y1060 \le X,Y \le 10^6

每组输出一行:先一个保留 1 位小数的面积,再依次输出 4 个整数——内部整点数、ABABBCBCACAC 边上的整点数,空格隔开。

样例

1
2
3
4
5
6
7
8
9
10
11
12
输入
0 0
2 0
0 2
0 0
3 0
0 3
-1

输出
2.0 0 1 1 1
4.5 1 2 2 2

思路讲解

一句话

整道题就是 Pick 定理 套一个 线段格点数公式,没有任何数据结构,纯数论 + 一次叉积。三件事:

  • 面积 = 叉积绝对值的一半;

  • 一条边上的整点数(不含端点) =gcd(dx,dy)1= \gcd(|dx|,|dy|) - 1

  • 内部整点数走 Pick 定理反解。

边上的整点数 = gcd − 1

把边的方向 (dx,dy)(dx,dy) 除掉公因子 g=gcd(dx,dy)g = \gcd(|dx|,|dy|),得到 本原向量 (dx/g,dy/g)(dx/g,\,dy/g)——它两个分量互质,是这条方向上「最短的整点步长」。整段恰好等于走 gg 个本原向量。

关键不变量:每跳一个本原向量正好落在一个整点上,跳 gg 步到终点。于是线段上一共 g+1g+1 个整点(含两端),掐掉两个端点就是 g1g-1 个。

为什么不多也不少,分两个方向(就像证等式要证两边):

  • 存在性:第 kk 个等分点坐标是 (kdx/g,kdy/g)(k \cdot dx/g,\, k \cdot dy/g),因为 dx/g,dy/gdx/g, dy/g 是整数,乘整数还是整数,所以这 g+1g+1 个点全是整点。

  • 唯一性:相邻两个等分点之间正好是一个本原向量(互质)。设其内部还有整点,则它满足 x/a=y/b=t(0,1)x/a = y/b = t \in (0,1),把 tt 写成最简分数 p/qp/q,需要 qaq \mid aqbq \mid b,于是 qgcd(a,b)=1q \mid \gcd(a,b) = 1,逼出 q=1q = 1tt 是整数,与 t(0,1)t \in (0,1) 矛盾。所以一个本原步内部挖不出整点。

主演示:拖蓝点 B 改线段,点 Step 沿本原向量一步步走。图中 (9,6)、g=3、本原向量 (3,2),线段内部有 g-1=2 个整点(绿点)。

Pick 定理求内部点

Pick 定理:面积 S=I+B21S = I + \frac{B}{2} - 1,其中 II 是内部整点数、BB 是边界整点数。反解出内部点:

I=2SB2+1I = \frac{2S - B}{2} + 1

  • 2S2S 直接用叉积绝对值,是整数,全程不碰浮点;

  • BB = 三条边的 gcd\gcd 之和(含顶点,见下一节)。

边界点 B 含不含顶点?含。

这是最容易混的地方,要分清两个量:

  • 输出给题目的「边上点数」:用 gcd1\gcd - 1不含顶点(题目明确要求);

  • Pick 用的边界点 BB:用 gcd\sum \gcd顶点。

绕多边形一圈,每条边的 gcd\gcd 恰好把它的「一个端点」算进来,三条边加起来每个顶点不重不漏算一次:

B=gcd=(顶点数)+(各边内部点数)B = \sum \gcd = (\text{顶点数}) + (\text{各边内部点数})

代码里我存的是 gcd - 1 这种「不含端点」的值,所以补回 3 个顶点:B=(dotAb+dotAc+dotBc)+3B = (dotAb + dotAc + dotBc) + 3。两个量差的正好是 3 个顶点,千万别混用。

三角形 + Pick 联动:拖顶点看三边 gcd-1、边界 B、面积 S、内部点 I 怎么变。图中 2S=47、B=3(即三个顶点)、I=23。

📎 动画与源码

下方附了一个交互式 HTML 演示:可以拖线段、点 Step 一步步看本原向量怎么把线段切成 gg 等份,还能拖三角形顶点看 Pick 各量联动。几张关键帧也截在这一节里。

AC 代码

牛客已 AC。核心全在 Solve():面积走几何模板的 polygonArea,边上点数用 __gcd,内部点 Pick 反解;输出顺序按题目要求是 AB,BC,ACAB, BC, AC

心路历程

调这道签到题反而栽了两个一模一样隐蔽的 off-by-one,而且两个样例都因为是「等腰直角三角形」太对称、把 bug 全藏住了:

  • Pick 反解少加 1:一开始写成 I = (2S - B + 1) / 2,正确是 I = (2S - B)/2 + 1 = (2S - B + 2)/2。少加的那个 1 在样例 1 里因为整数除法 -1/2 向零截断碰巧得 0、蒙混过关;样例 2 的 1/2 = 0 才露馅(期望是 1)。

  • 输出顺序 AC / BC 写反:题目要 AB,BC,ACAB, BC, AC,我手滑写成了 AB,AC,BCAB, AC, BC。两个样例三条边整点数全相等,交换看不出来;一旦数据是不规则三角形(ACBCAC \ne BC)立刻 WA。

教训:对称样例是 off-by-one 的天然伪装。这种题最好自己手造一个三边都不等、内部点也不为 0 的三角形(比如 A(1,1),B(8,2),C(3,8)A(1,1), B(8,2), C(3,8)2S=472S = 47B=3B = 3I=23I = 23)验一遍再交。

附件

segment_lattice_gcd.html.txt