LOJ2882 两个人的星座

两个人的星座

JOI 酱和 IOI 酱是一对亲密无间的好朋友。某天,JOI 酱与 IOI 酱决定去山上的某个观象台进行天体观测。

从观象台上可以观测到 \(N\) 颗星星,编号为 \(1\dots N\)。每颗星星的颜色为红色、蓝色、黄色中的一种。

在观象台上观测到的星星可以用坐标系上的点来表示。在坐标系上,\(i\) 号星对应的点为 \(P_i\),位于 \((X_i,Y_i)\)。坐标系上的点两两不同,且不存在三点共线。

JOI 酱和 IOI 酱想要设立一个叫做「JOIOI 座」的星座。首先。两个人决定使用红色、蓝色、黄色三种颜色的星各一个构成的三角形。他们将这样的三角形称作「好三角形」。

两人将满足以下条件的一对(两个,无序)好三角形作为「JOIOI 座的候补」:

  • 两个三角形没有公共点(包括内部和边界)。换言之,两个三角形之间既不相交,也不存在某个三角形包含另一个三角形。

JOI 酱和 IOI 酱想知道构成 JOIOI 座的候补一共有多少种方案。

注意,如果构成三角形的 \(6\) 个点一样,但是构成三角形的方式不同,算作不同的方案。

现在给出观象台上能观测到的星星的信息,请求出构成「JOIOI 座的候补」一共有多少种方案。

对于所有数据,\(6\le N\le 3000,\) \(-10^5\le X_i, Y_i\le 10^5,\) \(C_i=0\)\(1\)\(2\),保证任何一种颜色的星星都有至少一颗,星星坐标互不相同,无三星共线。

题解

http://jklover.hs-blog.cf/2020/04/13/Loj-2882-两个人的星座/#more

两个三角形如果相离,则一定可以用公切线分开。

枚举两个点的连线作为公切线,统计两个半平面中各类颜色点的数目,时间复杂度\(O(n^3)\)

优化一下,先枚举一个点作为原点,对其他点极角排序。

再枚举另一个点,用前缀和询问两个半平面中各类颜色点的数目。

由于两个相离的三角形通过顶点相连可以产生4根公切线,其中有2根对使得这对三角形被统计到,所以最后还需要将答案除以2。

时间复杂度\(O(n^2\log n)\)

IN int sign(int64 x){
	return x>=0;
}

struct point {int64 x,y;int c;};

IN point operator-(CO point&a,CO point&b){
	return {a.x-b.x,a.y-b.y,a.c};
}
IN int64 cross(CO point&a,CO point&b){
	return a.x*b.y-a.y*b.x;
}
IN bool operator<(CO point&a,CO point&b){
	return sign(a.y)!=sign(b.y)?sign(a.y)>sign(b.y):cross(a,b)>0;
}

CO int N=3e3+10;
int n;
point p[N],q[N];
int sum[N][3],tmp[2][3];

int query(int l,int r,int c){
	if(l>r) return 0;
	return sum[r][c]-sum[l-1][c];
}
int64 solve(){
	for(int i=2;i<=n;++i) q[i-1]=p[i]-p[1];
	sort(q+1,q+n);
	for(int i=1;i<n;++i)
		copy(sum[i-1],sum[i-1]+3,sum[i]),++sum[i][q[i].c];
	int64 ans=0;
	for(int i=1;i<n;++i){
		point iq=(point){0,0}-q[i];
		int l=i,r=lower_bound(q+1,q+n,iq)-q;
		for(int j=0;j<3;++j){
			tmp[0][j]=query(1,l-1,j)+query(r,n-1,j);
			tmp[1][j]=query(l+1,r-1,j);
		}
		int c=p[1].c,d=q[i].c;
		for(int cur=0;cur<2;++cur){
			int64 prod=1;
			for(int j=0;j<3;++j){
				if(c!=j) prod*=tmp[cur][j];
				if(d!=j) prod*=tmp[cur^1][j];
			}
			ans+=prod;
		}
	}
	return ans;
}
int main(){
	read(n);
	for(int i=1;i<=n;++i) read(p[i].x),read(p[i].y),read(p[i].c);
	int64 ans=0;
	for(int i=1;i<=n;++i){
		swap(p[1],p[i]);
		ans+=solve();
		swap(p[1],p[i]);
	}
	printf("%lld\n",ans/=2);
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/autoint/p/12789855.html