BZOJ4246: 两个人的星座

Description

JOI酱和IOI酱是好朋友。某天,JOI酱与IOI酱决定去山上的某个展望台进行天体观测。
从展望台上可以观测到N颗星星,编号为1...N。每颗星星的颜色为红色、蓝色、黄色中的一种。
在展望台上观测到的星星可以用坐标系上的点来表示。在坐标系上,星i(1<=i<=N)对应的点为Pi(Xi,Yi)。坐标系上的点两两不同,且不存在三点共线。
JOI酱和IOI酱想要设立一个叫做“JOIOI座”的星座。首先。两个人决定使用红色、蓝色、黄色三种颜色的星各一个构成的三角形。他们将这样的三角形称作“好三角形”。
两人将满足以下条件的好三角形无序二元组作为JOIOI座的候补:
两个三角形没有公共点(包括内部和边界)。换言之,两个三角形之间既不相交,也不存在某个三角形包含另一个三角形。
 
JOI酱和IOI酱想知道构成JOIOI座的候补一共有多少种方案。
注意如果构成三角形的6个点一样但是构成三角形的方式不同,算作不同的方案。
现在给出展望台上能观测到的星星的信息,请求出构成JOIOI座的候补一共有多少种方案

Input

第一行一个整数N,代表展望台上能观测到的星星的数量。
接下来N行,第i行(1<=i<=N)有三个空格分隔的整数Xi,Yi,Ci,表示星i的坐标为Pi(Xi,Yi),Ci表示星i的颜色,其中0代表红色,1代表蓝色,2代表黄色。

Output

输出一行一个整数,表示JOIOI座候补的方案数。

Sample Input

7
0 0 0
2 0 1
1 2 2
-2 1 0
-2 -3 0
0 -2 1
2 -2 2

Sample Output

4

HINT

样例中,JOIOI的候补有以下四种方案:

 

6<=N<=3000

-10^5<=Xi<=10^5(1<=i<=N)

-10^5<=Yi<=10^5(1<=i<=N)

0<=Ci<=2(1<=i<=N)

每种颜色的星至少存在一个

Pi≠Pj(1<=i<j<=N)

Pi,Pj,Pk不共线(1<=i<j<k<=N)

请注意你的常数

Source

计算几何
考虑两个三角形不相交
那么它们一定可以选出两对点连成的线把它们分割到两个半平面
枚举一个点,另一边按极角序排序,统计答案
最后答案要除以4,因为每对三角形被算了4次
#include <bits/stdc++.h>

using namespace std;

const int MAXN = 3030;
const double pi = acos( -1 );

struct Point
{
	int x, y, c, id;
	double k;
	bool operator < ( const Point &b ) const { return k < b.k; }
}p[MAXN], o;

int n, cnt[2][3], bl[MAXN];

long long ans, tmp;

int main()
{
	scanf( "%d", &n );
	for( int i = 1 ; i <= n ; i++ ) scanf( "%d%d%d", &p[ i ].x, &p[ i ].y, &p[ i ].c ), p[ i ].id = i;
	for( int i = 1 ; i <= n ; i++ )
	{
		for( int j = 1 ; j <= n ; j++ ) if( p[ j ].id == i ) { o = p[ j ]; break; }
		int col = o.c;
		for( int j = 1 ; j <= n ; j++ )
		{
			p[ j ].k = ( p[ j ].id == i ) ? 1e9 : atan2( p[ j ].y - o.y, p[ j ].x - o.x );
			if( p[ j ].k <= 0 ) p[ j ].k += pi;
		}
		memset( cnt, 0, sizeof cnt );
		sort( p + 1, p + n + 1 );
		for( int j = 1 ; j < n ; j++ )
			if( p[ j ].y < o.y || ( p[ j ].y == o.y && p[ j ].x > o.x ) ) cnt[ bl[ j ] = 0 ][ p[ j ].c ]++;
			else cnt[ bl[ j ] = 1 ][ p[ j ].c ]++;
		for( int j = 1 ; j < n ; j++ )
		{
			cnt[ bl[ j ] ][ p[ j ].c ]--;
			tmp = 1;
			if( col ) tmp *= cnt[ 0 ][ 0 ]; if( p[ j ].c ) tmp *= cnt[ 1 ][ 0 ];
			if( col ^ 1 ) tmp *= cnt[ 0 ][ 1 ]; if( p[ j ].c ^ 1 ) tmp *= cnt[ 1 ][ 1 ];
			if( col ^ 2 ) tmp *= cnt[ 0 ][ 2 ]; if( p[ j ].c ^ 2 ) tmp *= cnt[ 1 ][ 2 ];
			ans += tmp;
			tmp = 1;
			if( col ) tmp *= cnt[ 1 ][ 0 ]; if( p[ j ].c ) tmp *= cnt[ 0 ][ 0 ];
			if( col ^ 1 ) tmp *= cnt[ 1 ][ 1 ]; if( p[ j ].c ^ 1 ) tmp *= cnt[ 0 ][ 1 ];
			if( col ^ 2 ) tmp *= cnt[ 1 ][ 2 ]; if( p[ j ].c ^ 2 ) tmp *= cnt[ 0 ][ 2 ];
			ans += tmp;
			cnt[ bl[ j ] ^= 1 ][ p[ j ].c ]++; 
		}
	}
	cout << ans / 4 << endl;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值