1 条题解
-
0
题意回顾
我们需要计算在 的整数格点范围内,满足以下条件的凸多边形个数:
- 顶点为整点 ,且 。
- 多边形的任意一条边(不包含端点)不经过格点。
- 每条边的长度 且为整数。
- 凸多边形,不退化(无三点共线、无 角)。
- 输出结果对 取模。
数据范围:,。
关键转化
1. 边不经过格点的条件
边向量 必须满足 (即本原向量)。
2. 边长为整数的条件
必须是完全平方数,且 。
3. 凸多边形的向量表示
凸多边形可以看作按极角递增的边向量序列 满足:
- 向量和 (闭合)
- 相邻向量极角严格递增(保证凸且不退化)
- 所有向量来自满足上述条件的本原向量集合
算法步骤
步骤 1:生成所有合法边向量
枚举 ,,满足:
- 为整数(即 是完全平方数)
- 排除零向量
记这些向量集合为 ,大小 ,实际 时约几百个。
步骤 2:极角排序
将 中向量按极角()排序,便于后续 DP。
步骤 3:动态规划计数
定义 DP 状态:
- $dp[s][\text{sumX}][\text{sumY}][\text{minX}][\text{minY}][\text{maxX}][\text{maxY}]$ 表示:
- 已选 条边
- 当前向量和
- 坐标范围:,
但这样状态太大,需要优化。
优化思路:
- 我们只关心最终 的序列
- 坐标范围可以通过 等方式压缩
- 实际实现时,可以固定起点在原点,最后乘以平移方案数
步骤 4:平移方案计算
对于一个向量序列,设:
- ,
- ,
则需要的宽度 ,高度 。
平移方案数 = 。
步骤 5:最终计数
对每个长度 的合法向量序列(极角递增、和为零、相邻不共线),加上其平移方案数。
模 的处理
由于模数是 ,可以用
unsigned int自然溢出,或者用uint64_t计算后取模。
复杂度分析
- 向量数量
- DP 状态数与 、 相关,实际可运行
- 对于 ,经过优化的 DP 可以接受
代码框架(伪代码)
#include <bits/stdc++.h> using namespace std; using uint = unsigned int; int X, Y, K; uint mod = 1ULL << 32; struct Vec { int dx, dy; double angle; bool operator<(const Vec& other) const { return angle < other.angle; } }; vector<Vec> vecs; void generate_vectors() { for (int dx = -K; dx <= K; dx++) { for (int dy = -K; dy <= K; dy++) { if (dx == 0 && dy == 0) continue; int len2 = dx*dx + dy*dy; if (len2 > K*K) continue; int len = sqrt(len2); if (len*len != len2) continue; if (gcd(abs(dx), abs(dy)) != 1) continue; vecs.push_back({dx, dy, atan2(dy, dx)}); } } sort(vecs.begin(), vecs.end()); } uint solve() { generate_vectors(); int M = vecs.size(); // DP 初始化及转移 // 这里省略具体 DP 实现,它涉及多维状态 uint ans = 0; // 对每个合法序列计算平移方案并累加 return ans; } int main() { cin >> X >> Y >> K; cout << solve() << endl; return 0; }
总结
本题是一道结合计算几何、数论和动态规划的计数题,关键点在于:
- 将凸多边形条件转化为极角递增的本原向量序列
- 利用 和完全平方数条件筛选合法边向量
- 通过 DP 统计合法序列,并计算平移方案数
- 处理大范围 时只需知道坐标跨度
- 1
信息
- ID
- 5640
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者