[XJOI NOI02015训练题7] B 线线线 【二分】
题目分析
题意:过一个点 P 的所有直线,与点集 Q 的最小距离是多少?一条直线与点集的距离定义为点集中每个点与直线距离的最大值。
题解:二分答案,对于一个二分的距离,我们可以求出对于每个点的可用的极角范围,然后判断 n 个点的极角范围有没有交即可。
听起来非常简单..结果我发现细节很麻烦..
因为,极角的范围是环形的,如果限定在 [-PI, PI] 的范围内,跨越 -PI = PI 这条线的极角范围就很难处理。
然后两个环上的范围的交可能是两段,也是很难处理..
学习神犇的处理方式,对于每个极角范围,在左端点记上一个 1,右端点记上一个 -1,然后如果一个位置被 n 个区间包含,那么这个位置的前缀和就是 n 。
非常的和谐,看起来问题已经解决了...然而我发现神犇的做法还是有些细节无法理解..
比如区间的范围可能超出了 [-PI, PI] ....但是我已经想不清楚了...还是记住这种处理方式吧
照着神犇的代码写之后还是 TLE 了 2 个点,最后改了改 Eps 让二分次数减少了一些,终于过了。
并且向下保留 3 位小数,我这样写 printf("%.3f\n", Ans - 0.0005); 就会 WA 掉 1 个点。
这样写 AnsN = (int)(Ans * 1000); printf("%d.%03d\n", AnsN / 1000, AnsN % 1000); 才能 AC。
代码
#include <iostream> #include <cstdlib> #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> using namespace std; #define Vector Point #define PI 3.14159265358979 inline void Read_Int(int &Num) { char c = getchar(); bool Neg = false; while (c < '0' || c > '9') { if (c == '-') Neg = true; c = getchar(); } Num = c - '0'; c = getchar(); while (c >= '0' && c <= '9') { Num = Num * 10 + c - '0'; c = getchar(); } if (Neg) Num = -Num; } typedef double LF; inline LF gmin(LF a, LF b) {return a < b ? a : b;} inline LF gmax(LF a, LF b) {return a > b ? a : b;} inline LF Sqr(LF x) {return x * x;} const LF Eps = 1e-6; const int MaxN = 111111 + 5; int n, Top, Tot; LF dis[MaxN], ta[MaxN]; struct ES { LF Pos; int Num; ES() {} ES(LF a, int b) {Pos = a; Num = b;} } EQ[MaxN * 4]; inline bool Cmp(ES e1, ES e2) { return e1.Pos < e2.Pos; } struct Point { LF x, y; Point() {} Point(LF a, LF b) {x = a; y = b;} void Read() { int a, b; Read_Int(a); Read_Int(b); x = (LF)a; y = (LF)b; } } Px, P[MaxN]; inline LF Dis(Point p1, Point p2) { return sqrt(Sqr(p1.x - p2.x) + Sqr(p1.y - p2.y)); } bool Check(LF d) { LF l, r, t; Top = 0; Tot = n; for (int i = 1; i <= n; ++i) { if (dis[i] <= d) { --Tot; continue; } t = asin(d / dis[i]); l = ta[i] - t; r = ta[i] + t; EQ[++Top] = ES(l, 1); EQ[++Top] = ES(r, -1); if (ta[i] > 0) { EQ[++Top] = ES(l - PI, 1); EQ[++Top] = ES(r - PI, -1); } else { EQ[++Top] = ES(l + PI, 1); EQ[++Top] = ES(r + PI, -1); } } if (Top == 0) return true; int Cnt = 0; sort(EQ + 1, EQ + Top + 1, Cmp); for (int i = 1; i <= Top; ++i) { Cnt += EQ[i].Num; if (Cnt == Tot) return true; } return false; } int main() { scanf("%d", &n); Px.Read(); for (int i = 1; i <= n; ++i) { P[i].Read(); dis[i] = Dis(P[i], Px); ta[i] = atan2(P[i].y - Px.y, P[i].x - Px.x); } LF l, r, mid, Ans; l = 0; r = 2000000; while (r - l >= Eps) { mid = (l + r) / 2.0; if (Check(mid)) { Ans = mid; r = mid - Eps; } else l = mid + Eps; } int AnsN = (int)(Ans * 1000); printf("%d.%03d\n", AnsN / 1000, AnsN % 1000); return 0; }