【AT3526】[ARC082C] ConvexScore(贡献转化+容斥)
- 给定平面上\(n\)个点,如果一个大小为\(c\)的点集恰好构成凸包,假设这个凸包共包含\(s\)个点,则能产生\(2^{s-c}\)的贡献。求总贡献。
- \(n\le200\)
贡献转化
考虑\(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\n",s),0;
}
待到再迷茫时回头望,所有脚印会发出光芒