【AtCoder Regular Contest 082 E】ConvexScore
计算几何,数学。
题目大意:
给定平面上 $n$ 个点。从这些点中选择一些能构成凸包的点形成点集 $S$(必须都是顶点且内角小于 $180^\circ$)。
假设点集 $S$ 形成的凸包的内部、边上、顶点上共有 $k$ 个点,则 $S$ 的贡献为 $2^{k-|S|}$。
求所有这样的点集的贡献和。
题解:
考虑 $S$ 点集内部、边上的点形成的点集为 $T$,则 $S$ 的贡献为 $2^{|T|}$。这个的含义其实是 $T$ 的子集个数。
因此,相当于 $S$ 和 $T$ 中的任意一个子集的并集产生了 $1$ 的贡献。
由于一个点集的凸包是唯一的,所以这个贡献和其实相当于有凸包的点集的个数和。
我们考虑一个点集什么时候不存在凸包。仅当这些点都在同一直线上时,无法形成凸包。
一开始的总方案为 $2^n-n-1-\binom{n}{2}$(顶点任意选择,去掉 $0,1,2$ 个点的情况)。
然后我们考虑枚举两个点,确定一条直线,然后再枚举得到这条直线上还有的其它点个数 $t$,这些点任选都是不合法的,所以减去 $2^{t}-1$。
时间复杂度 $O(n^3)$。
Code:
#include<cstdio>
const int N=233,md=998244353;
struct point{
int x,y;
inline point operator-(const point&rhs)const{return(point){x-rhs.x,y-rhs.y};}
}a[N];
inline int cross(point a,point b){return a.x*b.y-a.y*b.x;}
int n,p2[N],ans;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d%d",&a[i].x,&a[i].y);
for(int i=*p2=1;i<=n;++i)p2[i]=p2[i-1]*2%md;
ans=(p2[n]-n-1LL-n*(n-1LL)/2%md+md*4LL)%md;
for(int i=1;i<n;++i)
for(int j=i+1;j<=n;++j){
int x=0;
for(int k=j+1;k<=n;++k)
x+=!cross(a[j]-a[i],a[k]-a[i]);
ans=(ans-p2[x]+1+md)%md;
}
printf("%d\n",ans);
return 0;
}