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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章