【luogu P7529】Permutation G(几何)(数学)(DP)

Permutation G

题目链接:luogu P7529

题目大意

给你二维平面上的一些点,问你有多少个排列,满足除了前三个点,其他点加入时与之前的点连边恰好有 3 条不会与其它线段相交,并连这三条边。

不会存在三点共线。

思路

首先你分析一下其实会发现你就要一直维护你选的点搞出来的图形在最外围是个三角形。

而且你发现如果你选了一个三角形,它里面的点其实按什么顺序随便选都可以。
那也就说我们可以设 fi,j,k 为搞好这个三角形的方案数。
然而你会发现转移根本转移不了,因为你搞出一个大的三角形会有一些新的可以任放的点,然后它的顺序其实可以跟前面任放的交错在一起。
那你就考虑多一维(反正你 n4 多设一维没什么问题),fi,j,k,x 为这个三角形,然后里面有 x 个点还没有选。

然后你就可以找里面的一个点,然后从小的三角形转移到这个大的。
然后转移大概就是你枚举你原来的小三角有多少个没有选,然后枚举现在有多少个没选,然后枚举新的任选点你有多少个不选,然后那组合数和阶乘搞一搞就可以了。
然后复杂度看似 O(n7),实则跑不满,可以过。
然后状态的转移方向看不懂直接上记忆化搜索。

然后这里讲讲数论的部分,就是关于一些操作要怎么搞。
判断一个点是否在一个三角形内:
如果有三角形 ABC,然后你要看 O 点,那只要 AOC+BOC+AOB=360° 就可以了。

如何求角度:
由于点都是在整点,其实可以用向量来求:
sinAOC=OAOB|OA||OB|

代码

#include<cmath> #include<cstdio> #include<algorithm> #define ll long long #define mo 1000000007 using namespace std; int n, x[41], y[41]; double Pi = acos(-1.0), eps = 1e-8; ll jc[41], C[41][41], num[41][41][41]; ll f[41][41][41][41]; bool rem[41][41][41]; struct vec { int x, y; double len() { return sqrt(x * x + y * y); } vec (int X = 0, int Y = 0) {x = X; y = Y;} int operator *(vec p) { return x * p.x + y * p.y; } }; double deg(int a, int b, int c) {//向量算角度 vec u(x[b] - x[a], y[b] - y[a]); vec v(x[c] - x[a], y[c] - y[a]); return acos(1.0 * (u * v) / u.len() / v.len()); } bool check_in(int a, int b, int c, int pl) {//判断是否在这个三角形内 return fabs(deg(pl, a, b) + deg(pl, a, c) + deg(pl, b, c) - 2 * Pi) < eps; } void DP(int i, int j, int k) { if (rem[i][j][k]) return ;//记忆化搜索 rem[i][j][k] = 1; for (int q = 0; q <= num[i][j][k]; q++)//直接选最大 f[i][j][k][q] = 6ll * C[num[i][j][k]][q] % mo * jc[num[i][j][k] - q] % mo; int c[3]; for (int pl = 1; pl <= n; pl++) {//从一个小的加一个点过来 if (pl == i || pl == j || pl == k || !check_in(i, j, k, pl)) continue; c[0] = pl; c[1] = j; c[2] = k; sort(c, c + 3); DP(c[0], c[1], c[2]);//下面两个跟这个其实一样,只是换掉的点不同了 for (int bn = 0; bn <= num[c[0]][c[1]][c[2]]; bn++) for (int su = 0; su <= num[i][j][k] - num[c[0]][c[1]][c[2]] - 1; su++)//这些是原本小三角没有的,然后看剩下多少个没选 for (int bu = 0; bu <= bn; bu++) f[i][j][k][su + bu] = (f[i][j][k][su + bu] + f[c[0]][c[1]][c[2]][bn] * C[bn][bu] % mo * C[num[i][j][k] - num[c[0]][c[1]][c[2]] - 1][su] % mo * jc[bn - bu + num[i][j][k] - num[c[0]][c[1]][c[2]] - 1 - su] % mo ) % mo; c[0] = i; c[1] = pl; c[2] = k; sort(c, c + 3); DP(c[0], c[1], c[2]); for (int bn = 0; bn <= num[c[0]][c[1]][c[2]]; bn++) for (int su = 0; su <= num[i][j][k] - num[c[0]][c[1]][c[2]] - 1; su++) for (int bu = 0; bu <= bn; bu++) f[i][j][k][su + bu] = (f[i][j][k][su + bu] + f[c[0]][c[1]][c[2]][bn] * C[bn][bu] % mo * C[num[i][j][k] - num[c[0]][c[1]][c[2]] - 1][su] % mo * jc[bn - bu + num[i][j][k] - num[c[0]][c[1]][c[2]] - 1 - su] % mo ) % mo; c[0] = i; c[1] = j; c[2] = pl; sort(c, c + 3); DP(c[0], c[1], c[2]); for (int bn = 0; bn <= num[c[0]][c[1]][c[2]]; bn++) for (int su = 0; su <= num[i][j][k] - num[c[0]][c[1]][c[2]] - 1; su++) for (int bu = 0; bu <= bn; bu++) f[i][j][k][su + bu] = (f[i][j][k][su + bu] + f[c[0]][c[1]][c[2]][bn] * C[bn][bu] % mo * C[num[i][j][k] - num[c[0]][c[1]][c[2]] - 1][su] % mo * jc[bn - bu + num[i][j][k] - num[c[0]][c[1]][c[2]] - 1 - su] % mo ) % mo; } } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d %d", &x[i], &y[i]); jc[0] = 1; for (int i = 1; i <= 40; i++) { jc[i] = (jc[i - 1] * i) % mo; } C[0][0] = 1; for (int i = 1; i <= 40; i++) { C[i][0] = 1; for (int j = 1; j <= 40; j++) C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mo; } for (int i = 1; i <= n; i++) for (int j = i + 1; j <= n; j++) for (int k = j + 1; k <= n; k++) for (int pl = 1; pl <= n; pl++) if (pl != i && pl != j && pl != k && check_in(i, j, k, pl)) num[i][j][k]++; for (int i = 1; i <= n; i++) for (int j = i + 1; j <= n; j++) for (int k = j + 1; k <= n; k++) if (num[i][j][k] == n - 3) { DP(i, j, k); printf("%lld", f[i][j][k][0]); return 0; } printf("0"); return 0; return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_P7529.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(42)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示