B
题目链接:2022 省选训练赛 Contest 05 C
题目大意
有一个二维平面上有些点,点有颜色,颜色有三种。
然后问你有多少个三角形对使得两个三角形都是由三个不同颜色的点组成,而且两个三角形无交。
思路
考虑怎样才会无交。
其实是要它们之间要有个内公切线。
也就是说,我们可以枚举点对,然后它们把这个二维平面分开。
然后一个点分别对应一个平面,然后统计要的颜色乘起来,就是 \(n^3\) 的做法了。
然后你考虑优化,先枚举一个点,考虑枚举第二个点的顺序。
不难想到一种方法:按极角排序,然后就像扫描线一样每次扫过去就只有一个点所属的平面边了(具体来讲应该是一个变成了第二个点,原本第二个点跑到下面)
然后你维护一下就好了,这个可以具体看看代码如何实现。
代码
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define rr register
using namespace std;
struct node {
int x, y, col;
double tmp;
}a[3001], b[3001], s;
int n, num[2][3], lst[3001];
ll ans;
bool op, opp;
node operator -(node x, node y) {
return (node){x.x - y.x, x.y - y.y, 0};
}
ll operator ^(node x, node y) {
return 1ll * x.x * y.y - 1ll * x.y * y.x;
}
bool cmp(node x, node y) {
return x.tmp < y.tmp;
}
int main() {
scanf("%d", &n);
for (rr int i = 1; i <= n; i++) {
scanf("%d %d %d", &a[i].x, &a[i].y, &a[i].col);
}
for (rr int i = 1; i <= n; i++) {
s = a[i];
for (rr int j = 1; j <= n; j++)
if (i != j) b[j - (j > i)] = a[j];
for (rr int j = 1; j < n; j++) {//先预先把极角求出来
node x = (b[j] - s);
if (x.y < 0) x.x = -x.x, x.y = -x.y;
b[j].tmp = atan2(x.y, x.x);
}
sort(b + 1, b + n, cmp);
memset(num, 0, sizeof(num));
opp = ((b[1] - s).y < 0);//记录是否上下记录翻转
for (rr int j = 2; j < n; j++) {//先把和第一个构成的两半分开
if (((b[j] - s) ^ (b[j] - b[1])) > 0) num[0][b[j].col]++, lst[j] = 0;
else num[1][b[j].col]++, lst[j] = 1;
}
ans += 1ll * num[0][(s.col + 1) % 3] * num[0][(s.col + 2) % 3] * num[1][(b[1].col + 1) % 3] * num[1][(b[1].col + 2) % 3];
for (rr int j = 2; j < n; j++) {
op = ((b[j] - s).y < 0);//判断这个的上下是否要翻转
num[lst[j]][b[j].col]--;
if (j != 2) num[lst[j - 1] ^ 1][b[j - 1].col]++;
else {
num[1][b[j - 1].col]++;//第一个你转肯定是到 1
}
ans += 1ll * num[0 ^ opp ^ op][(s.col + 1) % 3] * num[0 ^ opp ^ op][(s.col + 2) % 3] * num[1 ^ opp ^ op][(b[j].col + 1) % 3] * num[1 ^ opp ^ op][(b[j].col + 2) % 3];
}
}
printf("%lld", ans / 2);
return 0;
}