1 条题解

  • 0
    @ 2022-11-19 20:26:19

    可以将地面的轮廓拆分成多条轮廓直线来看,而每条轮廓直线只能被它上方的点看见。

    因此可以把每条直线看作一条向右指的向量,用半平面交得到所有向量的左半平面交出来的凸包,热气球离地面的高度就是该凸包和地面轮廓线的距离。

    不难发现形成这个最近距离的两点中一定至少有一个点是凸包上的拐点或地面上的拐点,因为如果两个点都不在端点上的话,往距离减小的那端移一定能使情况更优。

    因此可枚举凸包上的拐点和地面上的拐点,做一条过它的垂直于 x 轴的直线,求直线和另一个轮廓的交点与该点的距离,取最小即可。

    ​
    ​
    #include <bits/stdc++.h>
    #define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    using namespace std;
    using _T = double; //long long
    typedef double dd;
    constexpr _T eps = 1e-8;
    template<typename T> struct point {
        T x, y;
        bool operator== (const point& a) const { return (abs(x - a.x) <= eps && abs(y - a.y) <= eps); }
        point operator+ (const point& a) const { return { x + a.x,y + a.y }; }
        point operator- (const point& a) const { return { x - a.x,y - a.y }; }
        point operator* (const T k) const { return { k * x,k * y }; }
        T operator* (const point& a) const { return x * a.x + y * a.y; }
        T operator^ (const point& a) const { return x * a.y - y * a.x; }
        int toleft(const point& a) const { const auto t = (*this) ^ a; return (t > eps) - (t < -eps); }
    };
    ​
    using Point = point<_T>;
    ​
    struct argcmp
    {
        bool operator()(const Point& a, const Point& b) const
        {
            const auto quad = [](const Point& a)
            {
                if (a.y < -eps) return 1;
                if (a.y > eps) return 4;
                if (a.x < -eps) return 5;
                if (a.x > eps) return 3;
                return 2;
            };
            const int qa = quad(a), qb = quad(b);
            if (qa != qb) return qa < qb;
            const auto t = a ^ b;
            //if (abs(t)<=eps) return a*a<b*b-eps;
            return t > eps;
        }
    };
    ​
    template<typename T> struct line
    {
        point<T> p, v;
    ​
        bool operator==(const line& a) const { return v.toleft(a.v) == 0 && v.toleft(p - a.p) == 0; }
        int toleft(const point<T>& a) const { return v.toleft(a - p); }
        point<T> inter(const line& a) const { return p + v * ((a.v ^ (p - a.p)) / (v ^ a.v)); }//直线与直线的交点
        double dis(const point<T>& a) const { return abs(v ^ (a - p)) / v.len(); }
        point<T> proj(const point<T>& a) const { return p + v * ((v * (a - p)) / (v * v)); }//点在直线上的投影点
        bool operator<(const line& a) const
        {
            if (abs(v ^ a.v) <= eps && v * a.v >= -eps) return toleft(a.p) == -1;
            return argcmp()(v, a.v);
        }
    };
    ​
    using Line = line<_T>;
    ​
    template<typename T> struct polygon
    {
        vector<point<T>> p;
    ​
        size_t nxt(const size_t i) const { return i == p.size() - 1 ? 0 : i + 1; }
        size_t pre(const size_t i) const { return i == 0 ? p.size() - 1 : i - 1; }
    };
    ​
    using Polygon = polygon<_T>;
    ​
    template<typename T> struct convex : polygon<T>   //多数方法只支持凸包是逆时针的情况
    {
        convex operator+(const convex& c) const
        {
            const auto& p = this->p;
            vector<point<T>> e1(p.size()), e2(c.p.size()), edge(p.size() + c.p.size()), res;
            res.reserve(p.size() + c.p.size());
            for (size_t i = 0; i < p.size(); i++) e1[i] = p[i] - p[this->pre(i)];
            for (size_t i = 0; i < c.p.size(); i++) e2[i] = c.p[i] - c.p[c.pre(i)];
            rotate(e1.begin(), min_element(e1.begin(), e1.end(), argcmp()), e1.end());
            rotate(e2.begin(), min_element(e2.begin(), e2.end(), argcmp()), e2.end());
            merge(e1.begin(), e1.end(), e2.begin(), e2.end(), edge.begin(), argcmp());
            const auto cmp = [](const point<T>& u, const point<T>& v)
            {
                if (abs(u.y - v.y) <= eps) return u.x < v.x - eps;
                return u.y > v.y + eps;
            };
            const auto i0 = min_element(p.begin(), p.end(), cmp), j0 = min_element(c.p.begin(), c.p.end(), cmp);
            auto u = *i0 + *j0;
            const auto check = [](const vector<point<T>>& res, const point<T>& u)
            {
                const auto back1 = res.back(), back2 = *prev(res.end(), 2);
                return (back1 - back2).toleft(u - back1) == 0 && (back1 - back2) * (u - back1) >= -eps;
            };
            for (const auto& v : edge)
            {
                while (res.size() > 1 && check(res, u)) res.pop_back();
                res.push_back(u);
                u = u + v;
            }
            if (res.size() > 1 && check(res, res[0])) res.pop_back();
            return { res };
        }
        vector<T> sum;
    };
    ​
    using Convex = convex<_T>;
    vector<Line> _halfinter(vector<Line> l)//无解的时候返回空集
    {
        constexpr double LIM = 1e18;//添加的边界
        const auto check = [](const Line& a, const Line& b, const Line& c) {return a.toleft(b.inter(c)) < 0; };
        // const auto check=[](const Line &a,const Line &b,const Line &c)
        // {
        //     const auto t=b.v^c.v;
        //     const Point x=a.v*t,y=b.p*t+b.v*(c.v^(b.p-c.p))-a.p*t;
        //     return x.toleft(y)<0;
        // };
        l.push_back({ {-LIM,0},{0,-1} }); l.push_back({ {0,-LIM},{1,0} });
        l.push_back({ {LIM,0},{0,1} }); l.push_back({ {0,LIM},{-1,0} });
        sort(l.begin(), l.end());
        deque<Line> q;
        for (size_t i = 0; i < l.size(); i++)
        {
            if (i > 0 && l[i - 1].v.toleft(l[i].v) == 0 && l[i - 1].v * l[i].v > eps) continue;
            while (q.size() > 1 && check(l[i], q.back(), q[q.size() - 2])) q.pop_back();
            while (q.size() > 1 && check(l[i], q[0], q[1])) q.pop_front();
            q.push_back(l[i]);
        }
        while (q.size() > 1 && check(q[0], q.back(), q[q.size() - 2])) q.pop_back();
        while (q.size() > 1 && check(q.back(), q[0], q[1])) q.pop_front();
        if (q.size() <= 2 || check(q[1], q[0], q.back())) return vector<Line>();
        return vector<Line>(q.begin(), q.end());
    }
    ​
    Convex halfinter(const vector<Line>& l)
    {
        const auto lines = _halfinter(l);
        Convex poly; poly.p.resize(lines.size());
        if (lines.empty()) return poly;
        for (size_t i = 0; i < lines.size(); i++)
        {
            const size_t j = (i == lines.size() - 1 ? 0 : i + 1);
            poly.p[i] = lines[i].inter(lines[j]);
        }
        poly.p.erase(unique(poly.p.begin(), poly.p.end()), poly.p.end());
        if (poly.p.front() == poly.p.back()) poly.p.pop_back();
        return poly;
    }
    int dcmp(dd x, dd y) {
        if (fabs(x - y) < eps) return 0;
        if (x > y) return 1;
        return -1;
    }
    vector<Point> tmp;
    int main() {
        IOS;
        cout.setf(ios::fixed);
        int n; cin >> n;
        tmp.resize(n);
        for (int i = 0; i < n; i++) {
            cin >> tmp[i].x;
        }
        for (int i = 0; i < n; i++) {
            cin >> tmp[i].y;
        }
        vector<Line> L;
        for (int i = 0; i < n - 1; i++) {
            L.push_back({ tmp[i], tmp[i + 1] - tmp[i] });
        }
        Convex con = halfinter(L);
        dd ans = 1e18;
        for (int i = 0; i < con.p.size(); i++) {
            for (int j = 0; j < n - 1; j++) {
                if (dcmp(con.p[i].x, tmp[j].x) != -1 && dcmp(con.p[i].x, tmp[j + 1].x) != 1) {
                    Line ln = { tmp[j], tmp[j + 1] - tmp[j] };
                    Line ln2 = { con.p[i], {0, 1} };
                    Point pt = ln.inter(ln2);
                    ans = min(ans, fabs(pt.y - con.p[i].y));
                }
            }
        }
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < con.p.size() - 1; j++) {
                if (dcmp(tmp[i].x, con.p[j].x) != -1 && dcmp(tmp[i].x, con.p[j + 1].x) != 1) {
                    Line ln = { con.p[j], con.p[j + 1] - con.p[j] };
                    Line ln2 = { tmp[i], {0, 1} };
                    Point pt = ln.inter(ln2);
                    ans = min(ans, fabs(pt.y - tmp[i].y));
                }
            }
        }
        cout << setprecision(7) << ans << "\n";
    }
    

    信息

    ID
    80
    时间
    1000ms
    内存
    256MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者