THUPC2021-非欧几何 题解

发布于 2023-09-22  1464 次阅读


AI 摘要

标题:THUPC2021-非欧几何 题解 摘要:本文是关于THUPC2021初赛题目“非欧几何”的题解。题目要求在球面上确定一些圆,并给定点的坐标和球面的半径,需要判断这些点是否在安全区域、危险区域或中间区域。文章介绍了两种思路,一种是通过点与北极点的连线来描述平面,并将平面映射到二维平面上进行分析,另一种是直接计算点到给定圆的距离来判断它们的位置关系。文章提供了代码实现和详细的算法说明。 注意:本文摘要为机器生成,可能存在语法错误或不准确之处,仅供参考。

写在前面

首先是咱的Blog....因为一直以来的懒和不整修,在一位"好"朋友的帮助下...
咱原来部署在香港的服务成功宕机并且被锁定...无法申诉(x

所以目前的Blog的文章都失去了备份...咱只能重写并认识到了备份的重要性.jpg

在某只鸽子的邀请(×)胁迫(√)下,咱姑且看了几道他比较喜欢的THUPC2021的选题
也和他讨论这道题讨论了很久,故有了今天这份Write up

THUPC2021初赛-非欧几何

题目大意

欧几里德几何和非欧几里德几何
传统派vs维新派
路人怎么也被卷入进来了(吐槽)

Tinytree和ustze分别在一个球面上确定了一些圆,然后Kiana想知道一些点是否在安全区域、危险区域或者中间区域,球面的半径和点的坐标都给定,需要判断这些点的位置关系,和是否方便卡其脱离太

原题Link:https://www.luogu.com.cn/problem/P7138

思路

某只鸽子的思路大概是在球面上通过点与北极点的连线来描述平面,然后把平面映射到二维平面上再分析平面和球的关系

而咱构思的思路的话...类似于直接计算点到给定圆的距离来判断它们的位置关系
这样就不用把平面映射到二维平面上
但是要算三维坐标,如果实在是懒得不行的话...也可以学他直接映射下去算UV(

Code

#include <bits/stdc++.h>
#include <immintrin.h>
using namespace std;

#define gc (c = getchar())

template <typename T>
void chmin(T &x, const T &y) {
    if (x > y)
        x = y;
}

template <typename T>
void chmax(T &x, const T &y) {
    if (x < y)
        x = y;
}

typedef int64_t s64;
typedef uint64_t u64;
typedef uint32_t u32;
typedef pair<int, int> pii;

#define rep(i, l, r) for (int i = l; i <= r; ++i)
#define per(i, r, l) for (int i = r; i >= l; --i)
#define rep0(i, l, r) for (int i = l; i < r; ++i)

char readc() {
    char c;

    while (isspace(gc));

    return c;
}

int read() {
    char c;

    while (gc < '-');

    if (c == '-') {
        int x = gc - '0';

        while (gc >= '0')
            x = x * 10 + c - '0';

        return -x;
    }

    int x = c - '0';

    while (gc >= '0')
        x = x * 10 + c - '0';

    return x;
}

#undef gc

double sqr(const double &x) {
    return x * x;
}

int sgn(const double &x, const double &eps = 1e-9) {
    return x < -eps ? -1 : x > eps;
}

struct Point {
    double x, y;
};

Point operator +(const Point &a, const Point &b) {
    return {a.x + b.x, a.y + b.y};
}

Point operator -(const Point &a, const Point &b) {
    return {a.x - b.x, a.y - b.y};
}

double operator *(const Point &a, const Point &b) {
    return a.x * b.y - a.y * b.x;
}

Point operator *(const Point &a, const double &b) {
    return {a.x * b, a.y * b};
}

struct Line {
    Point p, v;
    double a;

    void calc() {
        a = atan2(v.y, v.x);
    }
};

bool left(const Point &p, const Line &l) {
    return l.v * (p - l.p) > 0;
}

Point pos(const Line &a, const Line &b) {
    double t = ((b.p - a.p) * b.v) / (a.v * b.v);
    return a.p + a.v * t;
}

const int N = 5000 + 10;

struct HalfPlanes {
    Point p[N], p1[N], p2[N];
    Line line[N], q[N];
    int cnt_line, n1, n2;

    void newLine(Point a, Point b) {
        if (sgn((b - a) * a) > 0)
            swap(a, b);

        line[++cnt_line] = {a, b - a};
    }

    void build() {
        rep(i, 1, cnt_line) line[i].calc();

        sort(line + 1, line + cnt_line + 1, [&](const Line &a, const Line &b) {
            return a.a < b.a;
        });

        int h = 1, t = 1;
        q[1] = line[1];

        rep(i, 2, cnt_line) {
            while (h < t && !left(p[t - 1], line[i]))
                --t;

            while (h < t && !left(p[h], line[i]))
                ++h;

            if (sgn(q[t].a - line[i].a) == 0)
                q[t] = left(q[t].p, line[i]) ? q[t] : line[i];
            else
                q[++t] = line[i];

            if (h < t)
                p[t - 1] = pos(q[t], q[t - 1]);
        }

        while (h < t && !left(p[t - 1], q[h]))
            --t;

        p[t] = pos(q[t], q[h]);

        if (t - h <= 1)
            return;

        int mn = h;

        rep(i, h, t)
        if (p[i].x < p[mn].x)
            mn = i;

        rotate(p + h, p + mn, p + t + 1);
        int m = h;

        while (m < t && p[m + 1].x >= p[m].x)
            ++m;

        rep(i, h, m) p1[++n1] = p[i];
        p2[++n2] = p[h];
        per(i, t, m) p2[++n2] = p[i];
        rep(i, 1, n2) p2[i].y *= -1;
    }

    bool check(Point p1[], int n1, Point p) {
        if (sgn(p.x - p1[1].x) < 0 || sgn(p.x - p1[n1].x) > 0)
            return 0;

        p.x += 1e-9;
        Point *i = lower_bound(p1 + 1, p1 + n1, p, [&](const Point &a, const Point &b) {
            return a.x < b.x;
        });
        p.x -= 1e-9;
        --i;
        return (*(i + 1) - *i) * (p - *i) > 0;
    }

    bool contain(const Point &p) {
        return n1 && check(p1, n1, p) && check(p2, n2, {p.x, -p.y});
    }
} halfPlanes[2];

int main() {
#ifdef kcz
    freopen("1.in", "r", stdin); 
    #endif

    int n[2], t;
    cin >> n[0] >> n[1] >> t;
    int R;
    cin >> R;

    auto read_point = [&]() -> Point {
        double x, y;
        scanf("%lf%lf", &x, &y);
        x /= R;
        y /= R;
        double z = sqrt(1 - sqr(x) - sqr(y));

        if (readc() == '-')
            z = -z;

        ++z;
        return {2 * x / (2 - z), 2 * y / (2 - z)};
    };

    rep(j, 0, 1) {
        rep(i, 1, n[j]) {
            Point a = read_point(), b = read_point();
            halfPlanes[j].newLine(a, b);
        }

        static const double U = 1e9;
        halfPlanes[j].newLine({-U, -U}, {U, -U});
        halfPlanes[j].newLine({U, -U}, {U, U});
        halfPlanes[j].newLine({U, U}, {-U, U});
        halfPlanes[j].newLine({-U, U}, {-U, -U});
        halfPlanes[j].build();
    }

    while (t--) {
        Point p = read_point();

        if (!halfPlanes[0].contain(p))
            puts("Safe");
        else if (!halfPlanes[1].contain(p))
            puts("Goodbye");
        else
            puts("Passer");
    }
}
届ける言葉を今は育ててる
最后更新于 2023-09-22