Loading

CF1284E New Year and Castle Construction

链接:https://www.luogu.com.cn/problem/CF1284E

题目描述:给定\(n\)个点,求\(4\)个点围住另外一个点\(5\)元子集个数。(保证任意三点不共线,不存在相同的点)

题解:我们可以考虑每一个点作为中间的\(p\)点的贡献,将其他的点极角排序,那么原问题其实就转化为了:在一个序列中取\(4\)个点且不存在任意一条经过原点的直线能将序列分成同一部分的方案数,那么将这\(4\)个点的极角排序后就有\(b-a<180\),\(c-b<180\),\(d-c<180,(a+360)-d<180\)$

这样的\(4\)元环不好求,考虑容斥:

\(1\).总方案:\(b-a<180\),\(c-b<180,d-c<180\),二分答案后转移成区间加,前缀和维护即可(当然这个其实就是选\(4\)个的方案数,其实就是\(C(n,4)\))

\(2\).不合法方案:\(b-a<180\),\(c-b<180\),\(d-c<180,(a+360)-d>180\),

可化为\(b-a<180\),\(c-b<180\),\(d-c<180,d-a<180\),

则仅需\(d-a<180\),这个同\(1\)维护就行了。

#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
long double t[2501];
long long n,ans,f[2501],X[2501],Y[2501],Sm[2501],length,sum[5][2501];
const long double Pi=asin(1)*2;
int read()
{
	char c=0;
	int sum=0,type=1;
	while ((c<'0'||c>'9')&&c!='-')
		c=getchar();
	if (c=='-')
	{
		type=-1;
		c=getchar();
	}
	while ('0'<=c&&c<='9')
	{
		sum=sum*10+c-'0';
		c=getchar();
	}
	return sum*type;
}
int main()
{
	int pos;
	n=read();
	for (int i=1;i<=n;++i)
		X[i]=read(),Y[i]=read();
	for (int i=1;i<=n;++i)
		Sm[i]=i*(i-1)/2;
	for (int i=1;i<=n;++i)
		Sm[i]+=Sm[i-1];	
	for (int i=1;i<=n;++i)
	{
		length=0;
		for (int j=1;j<=n;++j)
			if (i!=j) 
			{ 
				if (X[j]-X[i]>0&&Y[j]-Y[i]>=0)
					t[++length]=atan((long double)(1)*(Y[j]-Y[i])/(X[j]-X[i]));
				if (X[j]-X[i]>0&&Y[j]-Y[i]<0)
					t[++length]=atan((long double)(1)*(Y[j]-Y[i])/(X[j]-X[i]))+Pi*2;
				if (X[j]-X[i]==0)
					t[++length]=((Y[j]-Y[i])<0)*Pi+(Pi*0.5);
				if (X[j]-X[i]<0)
					t[++length]=atan((long double)(1)*(Y[j]-Y[i])/(X[j]-X[i]))+Pi;
			}
		sort(t+1,t+length+1);
		for (int j=1;j<=length;++j)
			sum[0][j]=1;
		pos=1;
		for (int j=1;j<=length;++j)
		{
			while (pos<length&&t[pos+1]-t[j]<Pi)
				pos++;
			f[j]=pos;
		}
		for (int k=1;k<=3;++k)
		{
			for (int j=1;j<=length;++j)
				sum[k][j]=0;
			for (int j=1;j<=length;++j)
			{
				sum[k][j+1]+=sum[k-1][j];
				sum[k][f[j]+1]-=sum[k-1][j];
			}
			for (int j=1;j<=length;++j)
				sum[k][j]+=sum[k][j-1];
		} 
		for (int j=1;j<=length;++j)
			ans+=sum[3][j];
		for (int j=1;j<=length;++j)
			ans-=Sm[max(f[j]-j-1,0ll)];
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2022-12-14 21:53  zhouhuanyi  阅读(36)  评论(0)    收藏  举报