BZOJ 1038. [ZJOI2008]瞭望塔
能看到其他所有点的区域就是轮廓线的半平面交。
然后最小高度就是半平面交与轮廓线这两个一次分段函数的差,极值肯定出现在分段点上,分别求一下即可。
#include <bits/stdc++.h> #define db double const db eps = 1e-9; inline int sign(db k) { return k < -eps ? -1 : k > eps; } inline int cmp(db k1, db k2) { return sign(k1 - k2); } struct P { db x, y; P() {} P(db x, db y): x(x), y(y) {} P operator + (const P &rhs) const { return P(x + rhs.x, y + rhs.y); } P operator - (const P &rhs) const { return P(x - rhs.x, y - rhs.y); } P operator * (const db &k) const { return P(x * k, y * k); } P operator / (const db &k) const { return P(x / k, y / k); } bool operator < (const P &rhs) const { int c = cmp(x, rhs.x); return c ? c == -1 : cmp(y, rhs.y) == -1; } bool operator == (const P &rhs) const { return !cmp(x, rhs.x) && !cmp(y, rhs.y); } db distTo(const P &rhs) const { return (*this - rhs).abs(); } db alpha() { return atan2(y, x); } void read() { scanf("%lf%lf", &x, &y); } void print() { printf("%.10f %.10f\n", x, y); } db abs() { return sqrt(abs2()); } db abs2() { return x * x + y * y; } P rot(const db &k) { return P(x * cos(k) - y * sin(k), x * sin(k) + y * cos(k)); } P rot90() { return P(-y, x); } P unit() { return *this / abs(); } P normal() { return rot90() / abs(); } int quad() { return sign(y) == 1 || (sign(y) == 0 && sign(x) >= 0); } db dot(const P &p) const { return x * p.x + y * p.y; } db det(const P &p) const { return x * p.y - y * p.x; } }; struct L { // ps[0] -> ps[1] P ps[2]; L() {} L(const P &p0, const P &p1) { ps[0] = p0; ps[1] = p1; } P &operator[](int i) { return ps[i]; } P dir() { return ps[1] - ps[0]; } bool include(const P &p) { return sign((ps[1] - ps[0]).det(p - ps[0])) > 0; } L push() { // push eps outawrd const db Eps = 1e-6; P delta = (ps[1] - ps[0]).normal() * Eps; return {ps[0] - delta, ps[1] - delta}; } }; #define cross(p1, p2, p3) ((p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y)) #define crossOp(p1, p2, p3) sign(cross(p1, p2, p3)) // 判断 p1p2 和 q1q2 是否相交 bool chkLL(const P &p1, const P &p2, const P &q1, const P &q2) { db a1 = cross(q1, q2, p1), a2 = -cross(q1, q2, p2); return sign(a1 + a2) != 0; } // 直线交点 P isLL(const P &p1, const P &p2, const P &q1, const P &q2) { assert(chkLL(p1, p2, q1, q2)); db a1 = cross(q1, q2, p1), a2 = -cross(q1, q2, p2); return (p1 * a2 + p2 * a1) / (a1 + a2); } P isLL(L l1, L l2) { return isLL(l1[0], l1[1], l2[0], l2[1]); } /***** 线段相交 *****/ bool intersect(db l1, db r1, db l2, db r2) { if (l1 > r1) std::swap(l1, r2); if (l2 > r2) std::swap(l2, r2); return !(cmp(r1, l2) == -1 || cmp(r2, l1) == -1); } bool isSS(const P &p1, const P &p2, const P &q1, const P &q2) { return intersect(p1.x, p2.x, q1.x, q2.x) && intersect(p1.y, p2.y, q1.y, q2.y) && crossOp(p1, p2, q1) * crossOp(p1, p2, q2) <= 0 && crossOp(q1, q2, p1) * crossOp(q1, q2, p2) <= 0; } bool isSS_strict(const P &p1, const P &p2, const P &q1, const P &q2) { return crossOp(p1, p2, q1) * crossOp(p1, p2, q2) < 0 && crossOp(q1, q2, p1) * crossOp(q1, q2, p2) < 0; } /***************/ /***** 点在线段上判定 *****/ bool isMiddle(db a, db m, db b) { return sign(a - m) == 0 || sign(b - m) == 0 || (a < m != b < m); } bool isMiddle(const P &a, const P &m, const P &b) { return isMiddle(a.x, m.x, b.x) && isMiddle(a.y, m.y, b.y); } bool onSeg(const P &p1, const P &p2, const P &q) { return crossOp(p1, p2, q) == 0 && isMiddle(p1, q, p2); } bool onSeg_strict(const P &p1, const P &p2, const P &q) { return crossOp(p1, p2, q) == 0 && sign((q - p1).dot(p1 - p2)) * sign((q - p2).dot(p1 - p2)) < 0; } /*******************/ // 投影 P proj(const P &p1, const P &p2, const P &q) { P dir = p2 - p1; return p1 + dir * (dir.dot(q - p1) / dir.abs2()); } // 反射 P reflect(const P &p1, const P &p2, const P &q) { return proj(p1, p2, q) * 2 - q; } // 最近点 db nearest(const P &p1, const P &p2, const P &q) { P h = proj(p1, p2, q); if (isMiddle(p1, h, p2)) return q.distTo(h); return std::min(p1.distTo(q), p2.distTo(q)); } // 线段距离 db disSS(const P &p1, const P &p2, const P &q1, const P &q2) { if (isSS(p1, p2, q1, q2)) return 0; return std::min(std::min(nearest(p1, p2, q1), nearest(p1, p2, q2)), std::min(nearest(q1, q2, p1), nearest(q1, q2, p2))); } // 夹角 db rad(const P &p1, const P &p2) { return atan2l(p1.det(p2), p1.dot(p2)); } // 多边形面积 db area(const std::vector<P> &ps) { db ans = 0; for (int i = 0, n = ps.size(); i < n; i++) ans += ps[i].det(ps[(i + 1) % n]); return ans; } // 点包含 2: inside 1: onSeg 0: outside int contain(const std::vector<P> &ps, const P &p) { int n = ps.size(), ret = 0; for (int i = 0; i < n; i++) { P u = ps[i], v = ps[(i + 1) % n]; if (onSeg(u, v, p)) return 1; if (cmp(u.y, v.y) <= 0) std::swap(u, v); if (cmp(p.y, u.y) > 0 || cmp(p.y, v.y) <= 0) continue; ret ^= crossOp(p, u, v) > 0; } return ret * 2; } // 凸包 std::vector<P> convexHull(std::vector<P> ps) { int n = ps.size(); if (n <= 1) return ps; std::sort(ps.begin(), ps.end()); std::vector<P> qs(n * 2); int k = 0; for (int i = 0; i < n; qs[k++] = ps[i++]) while (k > 1 && crossOp(qs[k - 2], qs[k - 1], ps[i]) <= 0) --k; for (int i = n - 2, t = k; i >= 0; qs[k++] = ps[i--]) while (k > t && crossOp(qs[k - 2], qs[k - 1], ps[i]) <= 0) --k; qs.resize(k - 1); return qs; } std::vector<P> convexHullNonStrict(std::vector<P> ps) { int n = ps.size(); if (n <= 1) return ps; std::sort(ps.begin(), ps.end()); std::vector<P> qs(n * 2); int k = 0; for (int i = 0; i < n; qs[k++] = ps[i++]) while (k > 1 && crossOp(qs[k - 2], qs[k - 1], ps[i]) < 0) --k; for (int i = n - 2, t = k; i >= 0; qs[k++] = ps[i--]) while (k > t && crossOp(qs[k - 2], qs[k - 1], ps[i]) < 0) --k; qs.resize(k - 1); return qs; } // 点集直径 db convexDiameter(const std::vector<P> &ps) { int n = ps.size(); if (n <= 1) return 0; int is = 0, js = 0; for (int k = 1; k < n; k++) is = ps[k] < ps[is] ? k : is, js = ps[js] < ps[k] ? k : js; int i = is, j = js; db ret = ps[i].distTo(ps[j]); do { if ((ps[(i + 1) % n] - ps[i]).det(ps[(j + 1) % n] - ps[j]) >= 0) (++j) %= n; else (++i) %= n; ret = std::max(ret, ps[i].distTo(ps[j])); } while (i != is || j != js); return ret; } // convecCut std::vector<P> convexCut(const std::vector<P> &ps, const P &q1, const P &q2) { std::vector<P> qs; int n = ps.size(); for (int i = 0; i < n; i++) { P p1 = ps[i], p2 = ps[(i + 1) % n]; int d1 = crossOp(q1, q2, p1), d2 = crossOp(q1, q2, p2); if (d1 >= 0) qs.push_back(p1); if (d1 * d2 < 0) qs.push_back(isLL(p1, p2, q1, q2)); } return qs; } // min_dis db min_dis(const std::vector<P> &ps, int l, int r) { if (r - l <= 5) { db ret = 1e100; for (int i = l; i < r; i++) for (int j = l; j < i; j++) ret = std::min(ret, ps[i].distTo(ps[j])); return ret; } int mid = l + r >> 1; db ret = std::min(min_dis(ps, l, mid), min_dis(ps, mid, r)); std::vector<P> qs; for (int i = l; i < r; i++) if (cmp(fabs(ps[i].x - ps[mid].x), ret) <= 0) qs.push_back(ps[i]); std::sort(qs.begin(), qs.end(), [](const P & a, const P & b) -> bool { return cmp(a.y, b.y) < 0; }); for (int i = 1; i < qs.size(); i++) for (int j = i - 1; j >= 0 && cmp(qs[j].y, qs[i].y - ret) >= 0; j--) ret = std::min(ret, qs[j].distTo(qs[i])); return ret; } // 圆的关系 int type(const P &o1, db r1, const P &o2, db r2) { db d = o1.distTo(o2); if (cmp(d, r1 + r2) == 1) return 4; // 相离 if (cmp(d, r1 + r2) == 0) return 3; // 外切 if (cmp(d, fabs(r1 - r2)) == 1) return 2; // 相交 if (cmp(d, fabs(r1 - r2)) == 0) return 1; // 内切 return 0; } bool parallel(L l0, L l1) { return sign(l0.dir().det(l1.dir())) == 0; } bool sameDir(L l0, L l1) { return parallel(l0, l1) && sign(l0.dir().dot(l1.dir())) == 1; } bool cmp(P a, P b) { if (a.quad() != b.quad()) { return a.quad() < b.quad(); } else { return sign(a.det(b)) > 0; } } bool operator < (L l0, L l1) { if (sameDir(l0, l1)) { return l1.include(l0[0]); } else { return cmp(l0.dir(), l1.dir()); } } bool check(L u, L v, L w) { return w.include(isLL(u, v)); } const int N = 1e3 + 7; L que[N]; std::vector<L> halfPlaneIS(std::vector<L> &l) { std::sort(l.begin(), l.end()); int head = 0, tail = 0; for (int i = 0; i < l.size(); i++) { if (i && sameDir(l[i], l[i - 1])) continue; while (tail - head > 1 && !check(que[tail - 2], que[tail - 1], l[i])) tail--; while (tail - head > 1 && !check(que[head + 1], que[head], l[i])) head++; que[tail++] = l[i]; } while (tail - head > 2 && !check(que[tail - 2], que[tail - 1], que[0])) tail--; while (tail - head > 2 && !check(que[1], que[0], que[tail - 1])) head++; std::vector<L> ans; for (int i = head; i < tail; i++) ans.push_back(que[i]); return ans; } db gety(P p, std::vector<P> point, std::vector<L> line) { int n = point.size(); if (sign(p.x - point[0].x) <= 0) return isLL(line[0], L(p, P(p.x, p.y + 10))).y; if (sign(p.x - point[n - 1].x) >= 0) return isLL(line[n], L(p, P(p.x, p.y + 10))).y; for (int i = 0; i < n - 1; i++) { if (isMiddle(point[i].x, p.x, point[i + 1].x)) return isLL(line[i + 1], L(p, P(p.x, p.y + 10))).y; } return 1e11; } db getyy(P p, std::vector<P> point) { for (int i = 0, sz = point.size(); i < sz - 1; i++) { if (isMiddle(point[i].x, p.x, point[i + 1].x)) return isLL(point[i], point[i + 1], p, P(p.x, p.y + 10)).y; } return 1e11; } int main() { int n; scanf("%d", &n); if (n <= 2) { puts("0"); return 0; } std::vector<P> p(n); for (int i = 0; i < n; i++) scanf("%lf", &p[i].x); for (int i = 0; i < n; i++) scanf("%lf", &p[i].y); std::vector<L> l; for (int i = 0; i < n - 1; i++) l.push_back(L(p[i], p[i + 1])); std::vector<L> half = halfPlaneIS(l); db ans = 1e10; std::vector<P> ss; for (int i = 0, sz = half.size(); i < sz - 1; i++) ss.push_back(isLL(half[i], half[i + 1])); for (int i = 0; i < n; i++) { ans = std::min(ans, std::fabs(gety(p[i], ss, half) - p[i].y)); } for (int i = 0, sz = half.size(); i < sz - 1; i++) { P pp = ss[i]; ans = std::min(ans, std::fabs(getyy(pp, p) - pp.y)); } printf("%.3f\n", ans); return 0; }