[ARC082E] ConvexScore
前言
是pb列表里面的小清新题目。
主要技巧是考虑分配贡献到什么地方会方便计数 。
题意
给定坐标系上 \(n\) 个点,对于每个可以构成凸包的点集 ,设点集大小是 \(S\) ,凸包上有 \(T\) 个点,则定义其贡献为 \(2^{S-T}\) ,求所有点集贡献之和。
\(n \le 200\) 。
难度
大概 \(2000\)
题解
你发现这个贡献就是一个凸包内部的点的子集。
直接考虑对子集计数?两个不同的凸包壳可能同时包含一个子集,难以计数!
如果把这个子集和外面这层凸包壳对应上的话,不同的凸包壳(就是在凸包上的点不同),即使内部子集相同,外部凸包壳也是不同的,所以如果把这个整体拿来计数,是不会算重的。
所以就是求有多少个点集可以找出一个凸包(意思就是不一定全在凸包上,内部也可以有一些点),考虑正难则反,空集,一个点,两个点是不能构成多边形的,所以是 \(2^n-1-n-\frac{n(n-1)}{2}\) ,然后考虑点数大于等于 \(3\) 的情况,枚举一条线段的两个端点,然后枚举有哪些点在这条直线上,判断方式就是求斜率或者向量叉乘,然后你求出在当前这条线段上有 \(k\) 个点共线,减去 \(2^k-1\) 即可(减一是因为排除空集的情况,空集在开始已经算过一次了)。
#include<iostream>
#define N 205
#define ll long long
#define p 998244353
using namespace std;
ll n,x[N],y[N],cf[N],ans;
int main()
{
scanf("%lld",&n);cf[0]=1;
for(int i=1;i<=n;i++)
{
cf[i]=cf[i-1]*2ll%p;
scanf("%lld%lld",&x[i],&y[i]);
}
ans=(cf[n]-1-n-n*(n-1)/2+p)%p;
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
ll now=0;
for(int k=j+1;k<=n;k++)
{
if((y[j]-y[i])*(x[k]-x[j])==(y[k]-y[j])*(x[j]-x[i])) now++;
}
ans=(ans-cf[now]+p+1)%p;
}
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号