[JOISC 2014 Day4] 两个人的星座
题意:
平面上有n(6 <= n <= 3000)个点,有三种颜色,求不相交的三色三角形对数。
解题思路:
如果两个三角形相交或者内含,则一定不存在内公切线
枚举每一个点为原点O,以该点建立极坐标系,并从小到大排序,并统计在该直线上方以及下方的各种点的个数
再循环取每个点P为边界(斜率从小到大),公切线上就有两个点O, P,然后分别枚举在内公切线上方以及下方的三角形,利用乘法原理
因为同一对三角形,同一条公切线会枚举两次,一对不相交的三角形存在两条内公切线,所以最后答案除4
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double pi = acos(-1.0);
const int maxn = 3e3 + 10;
ll ans = 0, cnt[2][5];
int n;
struct point {
int x, y, c;
double k;
double theta() {return atan2(y, x);}//atan2算倾斜角
bool operator < (const point & b) const {return k < b.k;}
} a[maxn], b[maxn];
int bl[maxn];
ll calc(int k, int x) {//k = 1 代表在当前线的上方, 当前颜色为x的不会被选到
//因为要三个点都是不相同的颜色
ll res = 1;
for (int i = 0; i < 3; i++)
if (x != i)
res *= cnt[k][i];
return res;
}
void solve(int u) {
int m = 0;
for (int i = 1; i <= n; i++) if (i != u) b[++m] = a[i];
for (int i = 1; i <= m; i++) {
b[i].x -= a[u].x, b[i].y -= a[u].y;
b[i].k = b[i].theta();
if (b[i].k <= 0) b[i].k += pi; //保证在(0, pi]
}
memset(cnt, 0, sizeof(cnt));
sort (b + 1, b + m + 1);
for (int i = 1; i <= m; i++) {
if (b[i].y < 0 || (b[i].y == 0 && b[i].x > 0))
bl[i] = 0;//在当前分割线下方
else
bl[i] = 1;
cnt[bl[i]][b[i].c]++;//统计直线上方
}
for (int i = 1; i <= m; i++) {//斜率从小到大枚举
cnt[bl[i]][b[i].c]--;
ans += calc(0, b[i].c) * calc(1, a[u].c);//分别枚举
ans += calc(1, b[i].c) * calc(0, a[u].c);//分别枚举
cnt[bl[i] ^= 1][b[i].c] ++;//改变内切线,则当前点会在下一条切线下方,所以要更新位置,以及数量
}
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i].x >> a[i].y >> a[i].c;
for (int i = 1; i <= n; i++)
solve(i);//枚举每个点为O
cout << (ans >> 2) << endl;
return 0;
}