[JOISC 2014 Day4] 两个人的星座(极角排序)

[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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值