题意:给你n个点,n个点的一个子集s组成一个凸多边形,凸多边形的边界点及其内部点的个数为k,边界点的个数为s,这个凸多边形的权值为2^(k-s),求n个点形成的所有的凸多边形的权值和。
思路:
考虑一个凸 s边形,其内与其上共 k 个点,那么它对最后答案的贡献是2^(k-s)。这意味着什么呢?就是除去顶点以外的 k−s个顶点取不取的所有情况数。
取 0 个,取 1个,取若干个,取所有,事实上这些所有情况的点的效果都是那一个凸 s 边形(因为已经固定取了那 s 个点作为顶点)。
所以可以说,对于任意 m个点,只要其中有一个子集能构成一个凸多边形,那么其对最后答案就有贡献。
贡献为多少呢?我们猜想是 1。所以究竟可不可能重复呢?是不可能的。因为对于给定的 m 个点,其最外面的一圈轮廓(…)是确定下来的,而顶点的 s个点也随之确定。
于是就得到了一个双射,问题就转化成了,有多少个点集满足其子集能构成凸多边形,也即有多少个点集不是所有点共线。
#include <bits/stdc++.h>
using namespace std;
#define MOD 998244353
int x[205], y[205], p[205];
int main(void)
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d%d", &x[i], &y[i]);
p[0] = 1;
for (int i = 0; i < n; i++)
p[i + 1] = (p[i] << 1) % MOD;
int ans = p[n] - n - 1;// 要组成凸多边形 所有情况减去取一个点的情况减去所有点都不取的情况
for (int i = 0; i < n; i++) {//固定一个点枚举其他点
for (int j = 0; j < i; j++) {//国定两个点
//p[0]表示两个点不能形成凸包减去
int cnt = 0;
for (int k = 0; k < j; k++) {
//x[i],x[j],x[k]三点共线,减去
if ((x[i] - x[j]) * (y[i] - y[k]) == (x[i] - x[k]) * (y[i] - y[j]))
cnt++;
}
ans =(ans-p[cnt]+MOD)%MOD;
}
}
printf("%d\n", ans);
return 0;
}