CF1906D题解

CF1906D

这里更容易进入且有翻译

题意

给定一个 n 个顶点的凸多边形,多次进行询问。每次询问给出两个不在多边形内的点 Pj1(Aj,Bj),Pj2(Cj,Dj),问能否找到一个点 P3,使线段 P1P3,P2P3 不与凸多边形相交(可以相切),并求最短的 |P1P3|+|P2P3|

解析

这题思路比较简单,但码量不小。

对于每次询问,先判断线段 P1P2 是否与凸多边形相交,若不相交直接输出 |P1P2| 即可;否则,可分别二分求 P1,P2 的切线(各两条,为以 P1,P2 为端点的射线),算出 P1 各条切线与 P2 切线的交点作为 P3 计算出 min{|P1Pi3|+|P2Pi3|} 作为答案,或者确认不存在这样一个交点 P3

对于二分求切线,如果点的横坐标不大于或不小于凸包上的任意点的横坐标,则把凸包分成上凸包和下凸包分别进行二分即可;否则,可按点的横坐标将凸包分割成左凸包和右凸包分别进行二分。

关于线段是否与凸多边形相交,将 P1,P2 视作点光源,判断 P1P2 光线的可见点集是否有交,若交集内有多于 1 个元素则线段不与凸包相交;或者如果交集内正好有 1 个元素点 Z,则可求 P1P2×P1ZP2P1×P2Z 进行判断。

代码

#include <bits/stdc++.h>
#include <unordered_map>
#define LL long long
#define pii pair<int, int>
#define pll pair<LL, LL>
#define double long double
#define pdd pair<double, double>
#define eps 1e-9

using namespace std;

//直线
struct line
{
    pdd p, v;

    line(pll p, pll v)
    {
        this->p = (pdd)p;
        this->v = (pdd)v;
    }
};

pll operator + (pll l, pll r)
{
    return make_pair(l.first + r.first, l.second + r.second);
}

pll operator - (pll l, pll r)
{
    return make_pair(l.first - r.first, l.second - r.second);
}

//点乘,下同
LL dot(pll l, pll r)
{
    return l.first * r.first + l.second * r.second;
}

//叉乘,下同
LL cross(pll l, pll r)
{
    return l.first * r.second - l.second * r.first;
}

pdd operator + (pdd l, pdd r)
{
    return make_pair(l.first + r.first, l.second + r.second);
}

pdd operator - (pdd l, pdd r)
{
    return make_pair(l.first - r.first, l.second - r.second);
}

double dot(pdd l, pdd r)
{
    return l.first * r.first + l.second * r.second;
}

//求两点距离
double dis(pdd l, pdd r)
{
    return sqrt(dot(l - r, l - r));
}

double cross(pdd l, pdd r)
{
    return l.first * r.second - l.second * r.first;
}

//求直线交点
pdd getNode(line l, line r)
{
    double s1 = cross(l.v, r.p - l.p), s2 = cross(l.v, r.p + r.v - l.p);
    return make_pair((r.p.first * s2 - (r.p.first + r.v.first) * s1) / (s2 - s1), (r.p.second * s2 - (r.p.second + r.v.second) * s1) / (s2 - s1));
}

int n;
pll ps[200005], ps1[100005];
map<pll, int> mp;

//二分求切点
pair<pll, pll> getPT(pll p)
{
    pair<pll, pll> res;

    if (p.first <= ps1[1].first)
    {
        int l = mp[ps1[1]], r = mp[ps1[n]];
        if (r < l)
            r += n;
        while (l < r)
        {
            int mid = l + r >> 1;
            if (cross(ps[mid + 1] - ps[mid], ps[mid + 1] - p) < 0)
                r = mid;
            else
                l = mid + 1;
        }
        res.first = ps[l];

        l = mp[ps1[n]], r = mp[ps1[1]];
        if (r < l)
            r += n;
        while (l < r)
        {
            int mid = l + r >> 1;
            if (cross(ps[mid + 1] - ps[mid], p - ps[mid]) > 0)
                l = mid + 1;
            else
                r = mid;
        }
        res.second = ps[l];
    }
    else if (p.first >= ps1[n].first)
    {
        int l = mp[ps1[n]], r = mp[ps1[1]];
        if (r < l)
            r += n;
        while (l < r)
        {
            int mid = l + r >> 1;
            if (cross(ps[mid + 1] - ps[mid], ps[mid + 1] - p) < 0)
                r = mid;
            else
                l = mid + 1;
        }
        res.first = ps[l];

        l = mp[ps1[1]], r = mp[ps1[n]];
        if (r < l)
            r += n;
        while (l < r)
        {
            int mid = l + r >> 1;
            if (cross(ps[mid + 1] - ps[mid], p - ps[mid]) > 0)
                l = mid + 1;
            else
                r = mid;
        }
        res.second = ps[l];
    }
    else
    {
        if (cross(ps1[1] - p, p - ps1[n]) < 0 || !cross(ps1[1] - p, p - ps1[n]) && !cross(ps[mp[ps1[n]] + 1] - ps1[n], ps1[1] - ps1[n]))
        {
            int L = mp[ps1[n]], R = mp[ps1[1]];
            if (R < L)
                R += n;
            int div = lower_bound(ps + L, ps + R + 1, p, greater<pll>()) - (ps + L);

            int l = L + div, r = R;

            if (r < l)
                r += n;
            while (l < r)
            {
                int mid = l + r >> 1;
                if (cross(ps[mid + 1] - ps[mid], ps[mid + 1] - p) < 0)
                    r = mid;
                else
                    l = mid + 1;
            }
            res.first = ps[l];

            l = L, r = L + div - 1;
            if (r < l)
                r += n;
            while (l < r)
            {
                int mid = l + r >> 1;
                if (cross(ps[mid + 1] - ps[mid], p - ps[mid]) > 0)
                    l = mid + 1;
                else
                    r = mid;
            }
            res.second = ps[l];
        }
        else
        {
            int L = mp[ps1[1]], R = mp[ps1[n]];
            if (R < L)
                R += n;
            int div = lower_bound(ps + L, ps + R + 1, p) - ps - L;

            int l = L + div, r = R;
            if (r < l)
                r += n;
            while (l < r)
            {
                int mid = l + r >> 1;
                if (cross(ps[mid + 1] - ps[mid], ps[mid + 1] - p) < 0)
                    r = mid;
                else
                    l = mid + 1;
            }
            res.first = ps[l];

            l = L, r = L + div - 1;
            if (r < l)
                r += n;
            while (l < r)
            {
                int mid = l + r >> 1;
                if (cross(ps[mid + 1] - ps[mid], p - ps[mid]) > 0)
                    l = mid + 1;
                else
                    r = mid;
            }
            res.second = ps[l];
        }
    }

    return res;
}

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

    int q;
    pll p0, p1;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> ps[i].first >> ps[i].second;
        ps1[i] = ps[n + i] = ps[i];
        mp[ps[i]] = i;
    }
    sort(ps1 + 1, ps1 + n + 1);

    cin >> q;
    while (q--)
    {
        cin >> p0.first >> p0.second >> p1.first >> p1.second;

        pair<pll, pll> res1 = getPT(p0), res2 = getPT(p1);

        double ans = 1e25; //初始值一定要开大
        int r1 = mp[res1.first], l1 = mp[res1.second], r2 = mp[res2.first], l2 = mp[res2.second];
        if (r1 < l1)
            r1 += n;
        if (r2 < l2)
            r2 += n;

        //判断线段是否与凸包相交
        if (!(l1 >= r2 || l2 >= r1))
            ans = dis(p0, p1);
        else if (l1 == r2 && cross(p0 - p1, ps[l1] - p1) >= 0)
            ans = dis(p0, p1);
        else if (l2 == r1 && cross(p1 - p0, ps[l2] - p0) >= 0)
            ans = dis(p0, p1);
        if (r1 > n && r2 <= n)
            l2 += n, r2 += n;
        else if (r2 > n && r1 <= n)
            l1 += n, r1 += n;
        if (!(l1 >= r2 || l2 >= r1))
            ans = dis(p0, p1);
        else if (l1 == r2 && cross(p0 - p1, ps[l1] - p1) >= 0)
            ans = dis(p0, p1);
        else if (l2 == r1 && cross(p1 - p0, ps[l2] - p0) >= 0)
            ans = dis(p0, p1);

        //求切线交点并计算答案
        if (cross(res1.first - p0, res2.first - p1))
        {
            pdd nd = getNode(line(p0, res1.first - p0), line(p1, res2.first - p1));
            if (dot((pdd)(res1.first - p0), nd - (pdd)p0) > eps && dot((pdd)(res2.first - p1), nd - (pdd)p1) > eps)
                ans = min(ans, dis(nd, p0) + dis(nd, p1));
        }
        if (cross(res1.first - p0, res2.second - p1))
        {
            pdd nd = getNode(line(p0, res1.first - p0), line(p1, res2.second - p1));
            if (dot((pdd)(res1.first - p0), nd - (pdd)p0) > eps && dot((pdd)(res2.second - p1), nd - (pdd)p1) > eps)
                ans = min(ans, dis(nd, p0) + dis(nd, p1));
        }
        if (cross(res1.second - p0, res2.first - p1))
        {
            pdd nd = getNode(line(p0, res1.second - p0), line(p1, res2.first - p1));
            if (dot((pdd)(res1.second - p0), nd - (pdd)p0) > eps && dot((pdd)(res2.first - p1), nd - (pdd)p1) > eps)
                ans = min(ans, dis(nd, p0) + dis(nd, p1));
        }
        if (cross(res1.second - p0, res2.second - p1))
        {
            pdd nd = getNode(line(p0, res1.second - p0), line(p1, res2.second - p1));
            if (dot((pdd)(res1.second - p0), nd - (pdd)p0) > eps && dot((pdd)(res2.second - p1), nd - (pdd)p1) > eps)
                ans = min(ans, dis(nd, p0) + dis(nd, p1));
        }

        if (ans >= 1e25)
            printf("-1\n");
        else
            printf("%.12LF\n", ans);
    }
}

最后祝各位顺利AC。>w<

posted @   OEAiHAN  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示