Codeforces 1146H. Satanic Panic
题目大意
- 给出平面内的
N
N
N个点,求多少种方案能选择五个点构成一个“五角星”。这里的“五角星”不一定要每条边相同,但要保证该线段之间该相交的地方要相交(脑补一下正常五角星的形状)。
-
N
≤
300
N≤300
N≤300
题解
- 首先可以想到转换一下题意,要求的是五角星,不太好统计,不妨可以把每个五角星对应成五个点顺次连接形成的凸包,也很显然每个五个点的凸包和五角星都是一一对应的,于是我们可以求这样的凸包数量。
- 考虑到凸包每条边的极角是依次递增的,所以不妨先把边连出来,一共
N
(
N
−
1
)
N(N-1)
N(N−1)条边(顶点相同方向不同也算不同),接着按照它们对应的向量的极角进行排序,用DP求方案数。
- 设
f
i
,
j
,
k
f_{i,j,k}
fi,j,k表示以
i
i
i为起点,凸包第
k
k
k个点是
j
j
j的方案数,显然有转移
-
f
i
,
i
,
0
=
1
f_{i,i,0}=1
fi,i,0=1
-
f
i
,
j
,
k
=
∑
(
j
′
,
j
)
∈
E
f
i
,
j
′
,
k
−
1
f_{i,j,k}=sum_{(j',j)in E} f_{i,j',k-1}
fi,j,k=(j′,j)∈E∑fi,j′,k−1
- 由于需要保证加入的每条边极角是递增的,所以要先枚举边再转移。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define N 310
#define ld long double
#define ll long long
struct node{
int x, y;
ld d;
}a[N], e[N * N];
ll f[N][N][6];
int cmp(node x, node y) {
return x.d < y.d;
}
int main() {
int n, i, j, l;
scanf("%d", &n);
for(i = 1; i <= n; i++) {
scanf("%d%d", &a[i].x, &a[i].y);
}
int tot = 0;
for(i = 1; i <= n; i++)
for(j = 1; j <= n; j++) if(i != j) {
e[++tot].x = i, e[tot].y = j;
e[tot].d = atan2(a[j].y - a[i].y, a[j].x - a[i].x);
}
sort(e + 1, e + tot + 1, cmp);
for(i = 1; i <= n; i++) f[i][i][0] = 1;
for(i = 1; i <= tot; i++) {
for(j = 1; j <= n; j++) {
for(l = 0; l < 5; l++) {
f[j][e[i].y][l + 1] += f[j][e[i].x][l];
}
}
}
ll ans = 0;
for(i = 1; i <= n; i++) ans += f[i][i][5];
printf("%lld", ans);
return 0;
}