Bzoj 3663: Crazy Rabbit

传送门: http://www.lydsy.com/JudgeOnline/problem.php?id=3663
题意: 兔子们决定在自己的城堡里安排一些士兵进行防守。 给出 n (1n2000) 个点的坐标,和城堡里一个圆心在原点的圆形的障碍,兔子们希望从中选出 n 个兔子,使得它们两两所在的直线都不与圆相交。 兔子们希望知道最多能选出多少兔子。
题解: 两个兔子连线不于圆交等价于两只兔子的的极角序区间有交但不包含。脑补一下:两个极角序区间如果刚好在边缘相交就是刚好两点连线和圆相切,相交多一点就相离,相交少一点就相交; 两个极角序区间如果是包含关系并且一个边缘相同两点连线延长线和圆相切,内部区间移进去连线延长线和圆相交,内部区间往外移连线延长线和圆相离。
  因为我们只要判定两极角序区间是交或者相离或包含,那么我们对一个区间端点移动 2π 或者区间取补集是不影响这个判定的。我们先把区间通过这两个操作整理到 (π,π) 内,按左端点排序,然后枚举第一个区间,后面的区间因为要和枚举的区间有交并且不包含,所以左端点要在区间里,右端点要在区间外,因为我们只取出了左端点在枚举的区间内的区间,所以这些区间取出来肯定是互相都有交的,为了保证它们不存在包含关系,所以在这些区间里要选出右端点递增的区间,就变成了关于右端点的LIS问题了。复杂度 O(n2logn)

#include<bits/stdc++.h>
const int N = 2333;
const double Pi = acos(-1);
struct rec{double l, r;} p[N];
int n, _n, R, x, y, cnt, ans;
double d, c, deg, a[N], h[N];
bool cmp(const rec &a, const rec &b) {return a.l < b.l;}
int LIS() {
    if (!_n) return 0;
    h[cnt = 1] = a[1];
    for (int i = 2; i <= _n; i++) {
        if (a[i] > h[cnt]) {h[++cnt] = a[i]; continue;};
        int l = 1, r = cnt;
        while (l < r) {
            int mid = (l + r) >> 1;
            if (h[mid] < a[i])
                l = mid + 1;
            else
                r = mid;
        }
        h[l] = a[i];
    }
    return cnt;
}
int main() {
    scanf("%d%d", &n, &R);
    for (int i = 1; i <= n; i++) {
        scanf("%d%d", &x, &y);
        d = sqrt(x * x + y * y);
        deg = atan2(y , x);
        c = acos(R / d);
        p[i].l = deg - c;
        p[i].r = deg + c;
        while (p[i].l < -Pi) p[i].l += Pi * 2;
        while (p[i].r >  Pi) p[i].r -= Pi * 2;
        if (p[i].l > p[i].r) std::swap(p[i].l, p[i].r);
    }
    std::sort(p + 1, p + n + 1, cmp);
    for (int i = 1; i <= n; i++) {
        _n = 0;
        for (int j = i + 1; j <= n; j++)
            if (p[j].l < p[i].r && p[j].r > p[i].r)
                a[++_n] = p[j].r;
        ans = std::max(ans, LIS() + 1);
    }
    printf("%d\n", ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值