用 Pick 定理数三角形 (0,0),(n,m),(p,0) 内部格点:2S=pm,边界 B=gcd(n,m)+gcd(|p-n|,m)+p。
OJ: roj
题目 ID: 2059
难度:普及+/提高-
标签:数学计算几何Pick定理数论gcdpython
创建: 2026-10-01 06:07
更新: 2026-10-07 13:50
形式化题目
给定三个顶点
输入一行三个整数
关于原题面
本题在 ROJ 题面页上残留了 USACO「fence3(Electric Fences)」的描述,但配套的
12 组测试数据(文件名 fence90 ~ fence911,配置标题「usaco-3.4.3」)全部是
USACO 3.4「fence9」的数据,即本题实际要解的「三角形内部整点数」。本题解按真实
测试数据编写。
正解
思路
朴素做法与它的瓶颈
最直接的做法是圈出三角形的包围盒
包围盒规模最坏是
关键观察:顶点全为格点时,Pick 定理把计数变成代数
Pick 定理说:对顶点全为格点的简单多边形,
其中
面积是
因为
边界整点数只需三次 gcd。 端点为整点的线段
| 边 | 整点个数(含两端) | 举例( |
|---|---|---|
| $\gcd( | p-n | |
三边相加时,三个顶点各被相邻两条边数了一次,共重复 3 次,减掉:
以测试数据 fence91(
合成最终公式
把
Pick 定理保证分子必为偶数,所以直接用整数除法 // 就是精确答案。
下面画出 ○ 是边上的整点、● 是内部整点、· 是外部:
可以看到边界恰好 8 个点,内部只有
y=2 · · ○ · ·
y=1 · ○ ● ○ ·
y=0 ○ ○ ○ ○ ○
x0 x1 x2 x3 x4代入公式验证:● 吻合。
这张图把
代码
Python 版:
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-10-01 06:07
# update_at: 2026-10-01 06:07
import sys
from math import gcd
def interior_points(n: int, m: int, p: int) -> int:
"""返回三角形 (0,0),(n,m),(p,0) 内部(不含边界)的整点数。
Pick 定理 I = S - B/2 + 1;这里改用两倍量避免小数:
I = (2S - B + 2) / 2,其中 2S = p*m,B 是三条边上的整点数之和。
"""
area2 = p * m # 鞋带公式的两倍面积
boundary = gcd(n, m) + gcd(abs(p - n), m) + p # 三条边的 gcd 之和
return (area2 - boundary + 2) // 2
def solve() -> None:
n, m, p = map(int, sys.stdin.buffer.read().split())
print(interior_points(n, m, p))
if __name__ == "__main__":
solve()C++ 版(同一算法):
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-06 10:30
* update_at: 2026-10-06 10:30
*/
// usaco-3.4.3 fence9:数三角形 (0,0),(n,m),(p,0) 内部的整点数。
// Pick 定理 S = I + B/2 - 1,移项并用两倍量避免小数:
// 2S = p * m
// B = gcd(n,m) + gcd(|p-n|,m) + p (三条边上的整点数,顶点重复计数已扣掉)
// I = (2S - B + 2) / 2
#include <cstdio>
using namespace std;
typedef long long ll;
ll n, m, p; // 三角形三个顶点 (0,0)、(n,m)、(p,0)
// 欧几里得算法求最大公约数,供统计边界整点数使用
ll gcd_ll(ll a, ll b) {
while (b != 0) {
ll t = a % b;
a = b;
b = t;
}
return a;
}
int main() {
scanf("%lld %lld %lld", &n, &m, &p);
ll area2 = p * m; // 鞋带公式给出的两倍面积
ll boundary = gcd_ll(n, m) + gcd_ll(p - n < 0 ? n - p : p - n, m) + p; // 三边整点数
ll interior = (area2 - boundary + 2) / 2; // Pick 定理求内部整点数
printf("%lld\n", interior);
return 0;
}复杂度
- 时间:
,即三次 gcd;在本题上限下几乎是常数,实测单组约 0.018s。 - 空间:
。
用真实数据验证:12 组数据(含
总结
顶点全为格点的多边形,「内部整点数」不需要逐点枚举:Pick 定理