1 条题解
-
0
题目分析
这是一个计算几何与动态规划结合的题目。我们需要将一个凸多边形划分成若干个三角形,要求每个三角形内部包含偶数只羊,求划分方案数。
关键点
- 凸多边形性质:顶点按顺时针顺序给出,且羊严格在多边形内部
- 三角形划分:划分线只能在顶点相交,不能相交于多边形内部
- 偶数约束:每个三角形必须包含偶数只羊
核心思路
- 预处理可行性:对于多边形的每条边
(i,j),判断以该边为三角形的一条边时,三角形(i,k,j)是否满足包含偶数只羊的条件 - 动态规划:使用区间DP计算划分方案数
算法步骤
-
预处理 f[i][j]:
- 对于每个顶点 i,以它为原点,对其他顶点和羊按极角排序
- 对于边 (i,j),统计在向量
a[j]-a[i]左侧的羊的数量 - 如果羊的数量是偶数且没有羊恰好在边界上,则 f[i][j] = 1,否则为 0
-
动态规划 dp[i][j]:
- dp[i][j] 表示从顶点 i 到 j 的子多边形的划分方案数
- 初始条件:相邻顶点 dp[i][i+1] = 1
- 状态转移:枚举中间顶点 k,如果 f[i][k] 和 f[k][j] 都为 1,则
dp[i][j] += dp[i][k] * dp[k][j]
复杂度分析
- 预处理:O(n² log m)
- 动态规划:O(n³)
- 总复杂度:O(n³),对于 n ≤ 600 可以接受
代码实现
// ==================================== // author: M_sea // website: https://m-sea-blog.com/ // ==================================== #include <bits/stdc++.h> #define file(x) freopen(#x".in","r",stdin); freopen(#x".out","w",stdout) using namespace std; typedef long long ll; int read() { int X = 0, w = 1; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') w = -1; c = getchar(); } while (c >= '0' && c <= '9') X = X * 10 + c - '0', c = getchar(); return X * w; } const int N = 600 + 10, M = 20000 + 10; int n, m, mod; int f[N][N], dp[N][N]; struct Point { int x, y; } a[N], b[M], O; Point operator -(Point a, Point b) { return (Point) { a.x - b.x, a.y - b.y }; } int cross(Point a, Point b) { return a.x * b.y - a.y * b.x; } int main() { n = read(), m = read(), mod = read(); for (int i = 1; i <= n; ++i) a[i] = (Point) { read(), read() }; for (int i = 1; i <= m; ++i) b[i] = (Point) { read(), read() }; // 预处理每条边是否可行 for (int i = 1; i <= n; ++i) { O = a[i]; // 以顶点i为原点,按极角排序 sort(b + 1, b + m + 1, [](Point x, Point y) { return cross(x - O, y - O) < 0; }); for (int j = i + 1, p = 0; j <= n; ++j) { // 统计在边(i,j)左侧的羊的数量 while (p < m && cross(a[j] - a[i], b[p + 1] - a[i]) > 0) ++p; // 如果羊的数量是偶数且没有羊在边界上,则该边可行 f[i][j] = !((p & 1) || (p < m && cross(a[j] - a[i], b[p + 1] - a[i]) == 0)); } } // 动态规划 for (int i = 1; i < n; ++i) dp[i][i + 1] = 1; for (int l = 3; l <= n; ++l) for (int i = 1; i + l - 1 <= n; ++i) { int j = i + l - 1; for (int k = i + 1; k < j; ++k) if (f[i][k] && f[k][j]) dp[i][j] = (dp[i][j] + 1ll * dp[i][k] * dp[k][j]) % mod; } printf("%d\n", dp[1][n]); return 0; }总结
本题的关键在于:
- 利用极角排序快速统计每条边一侧的羊的数量
- 理解几何关系:通过叉积判断点与线的位置关系
- 区间DP的应用:将多边形划分问题转化为子问题求解
这个解法巧妙地结合了计算几何和动态规划,预处理阶段通过排序优化统计过程,动态规划阶段则利用子问题的最优解构造原问题的解。
- 1
信息
- ID
- 3287
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 5
- 已通过
- 1
- 上传者