传送门: http://www.lydsy.com/JudgeOnline/problem.php?id=3663
题意: 兔子们决定在自己的城堡里安排一些士兵进行防守。 给出
n (1≤n≤2000)
个点的坐标,和城堡里一个圆心在原点的圆形的障碍,兔子们希望从中选出
n
个兔子,使得它们两两所在的直线都不与圆相交。 兔子们希望知道最多能选出多少兔子。
题解: 两个兔子连线不于圆交等价于两只兔子的的极角序区间有交但不包含。脑补一下:两个极角序区间如果刚好在边缘相交就是刚好两点连线和圆相切,相交多一点就相离,相交少一点就相交; 两个极角序区间如果是包含关系并且一个边缘相同两点连线延长线和圆相切,内部区间移进去连线延长线和圆相交,内部区间往外移连线延长线和圆相离。
因为我们只要判定两极角序区间是交或者相离或包含,那么我们对一个区间端点移动
#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;
}