[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;
}    
posted @ 2022-08-17 11:00  Constant1227  阅读(59)  评论(1)    收藏  举报