- 给定平面上(n)个点,如果一个大小为(c)的点集恰好构成凸包,假设这个凸包共包含(s)个点,则能产生(2^{s-c})的贡献。求总贡献。
- (nle200)
贡献转化
考虑(2^{s-c})是个什么东西,发现就意味着构成凸包的这(c)个点必选,剩下的(s-c)个点可选可不选。
于是限制就从原本恰好构成凸包变成能够构成凸包,且每个能够构成凸包的点集贡献都是(1)。
简单容斥
我们用点集总数(2^n)减去选择点数小于等于(2)的方案数(C_n^0+C_n^1+C_n^2),再减去选择大于等于(3)个点且这些点共线的方案数。
考虑枚举这些点中编号最小的两个(p_i,p_j),枚举之后每个点假设有(w)个与它们贡献,那么产生的非法方案数对应的就应该是(2^w-1),从总答案中减去即可。
代码:(O(n^3))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 200
#define X 998244353
using namespace std;
int n;struct P {int x,y;}p[N+5];I int gcd(CI x,CI y) {return y?gcd(y,x%y):x;}
int main()
{
RI i,j,k;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d%d",&p[i].x,&p[i].y);
RI s=1;for(i=1;i<=n;++i) s=2LL*s%X;s=((s-1-n-1LL*n*(n-1)/2)%X+X)%X;//总方案数减去选择点数≤2的方案数
RI t,g,tx,ty,kx,ky;for(i=1;i<=n;++i) for(j=i+1;j<=n;++j)//枚举编号最小的两个点
{
if(p[j].x==p[i].x) {for(t=1,k=j+1;k<=n;++k) p[k].x==p[i].x&&(t=2LL*t%X);goto End;} //x坐标相同
if(p[j].y==p[i].y) {for(t=1,k=j+1;k<=n;++k) p[k].y==p[i].y&&(t=2LL*t%X);goto End;}//y坐标相同
for(g=gcd(p[j].x-p[i].x,p[j].y-p[i].y),tx=(p[j].x-p[i].x)/g,ty=(p[j].y-p[i].y)/g,t=1,k=j+1;k<=n;++k)//一般情况
!((p[k].x-p[i].x)%tx)&&!((p[k].y-p[i].y)%ty)&&(p[k].x-p[i].x)/tx==(p[k].y-p[i].y)/ty&&(t=2LL*t%X);
End:s=(s-(t-1)+X)%X;//减去非法情况数
}return printf("%d
",s),0;
}