3295. 星际旅行(计算几何)

题意:n维空间中存在一个超维球,求2点不通过圆(可以经过球表面)的最短距离

解:两点确定一条直线,3点确定一个平面,有了球心和另外两点就可以确定出一个2维平面,直接以球心为原点,其中一个点为X轴上一点建2维坐标系(这个点的坐标为(它和球心的距离, 0))。之后就是求线段和圆的位置关系了。

↓直接从这里借(tou)的图↓

image

建完系后得到三点,圆心O和两点AB。三角形AOB面积就是\(\frac{\overrightarrow{OA} \times \overrightarrow{OB}}{2}\),然后就得到O到AB直线的距离\(h = \frac{\overrightarrow{OA} \times \overrightarrow{OB}}{\left|AB\right|}\)

\(h>=R\)时,答案就是\(\left|AB\right|\)

\(h<R\)时,当ABO或BAO为钝角时(前提两点都在圆外),线段不经过圆,同上;

否则答案为两条切线+一段弧。

最后要输出每个点到其他所有点的距离,所以要预处理一下每两点之间的直线距离。

#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
#define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
using namespace std;

const ll mod = 998244353;
const int maxn = 1e4 + 5;

double pi = acos(-1);
const double eps = 1e-10;

inline int dcmp(double x)//cmp x with 0
{
    if (fabs(x) <= eps)return 0;
    return x < 0 ? -1 : 1;
}
inline int cmp(double x, double y)
{
    //x>y return 1
    //x<y reutrn -1
    //x==y return 0
    return dcmp(x - y);
}

int sgn(double x)
{
    if (fabs(x) < eps)return 0;
    else return x < 0 ? -1 : 1;
}

struct Point {
    double x, y;
    Point(double xx, double yy) { x = xx; y = yy; }
    Point operator -(Point s) { return Point(x - s.x, y - s.y); }
    Point operator +(Point s) { return Point(x + s.x, y + s.y); }
    double operator *(Point s) { return x * s.x + y * s.y; }
    Point operator * (double p) { return Point(x * p, y * p); }//向量乘数
    double operator ^(Point s) { return x * s.y - y * s.x; }
    Point operator /(double k) { return Point(x / k, y / k); }
    bool operator ==(Point s) { return sgn(x - s.x) == 0 && sgn(y - s.y) == 0; }
};

typedef Point Vector;

double Cross(Point a, Point b, Point c)
{
    return (b - a) ^ (c - a);
}
double Cross(Vector A, Vector B) {
    return A.x * B.y - A.y * B.x;
}


double zb[2005][105];//点坐标
int n, m;
double dis[2005][2005];//两两之间的直线距离

double get_dis(int x, int y)//返回直线距离
{
    if (dis[x][y] > eps)return dis[x][y];
    double res = 0;
    for (int i = 1; i <= n; i++)
        res += (zb[x][i] - zb[y][i]) * (zb[x][i] - zb[y][i]);
    return sqrt(res);
}



int main()
{
    fastio;
    cin >> n >> m;
    double r;
    cin >> r;
    for (int i = 1; i <= n; i++)cin >> zb[0][i];
    for (int i = 1; i <= m; i++)
        for (int j = 1; j <= n; j++)
        {
            cin >> zb[i][j];
            zb[i][j] -= zb[0][j];//以圆心为原点映射
        }
    for (int j = 1; j <= n; j++)
        zb[0][j] = 0;
    vector<double>ans(m + 1, 0);
    Point O(0, 0);
    for (int i = 0; i <= m; i++)
        for (int j = i + 1; j <= m; j++)
            dis[i][j] = dis[j][i] = get_dis(i, j);
    for (int i = 1; i <= m; i++)
    {
        for (int j = i + 1; j <= m; j++)
        {
            double a = get_dis(0, i);
            double b = get_dis(0, j);
            double c = get_dis(i, j);//三条边距离
            Point A(a, 0);//A点坐标
            double cAOB = (a * a + b * b - c * c) / (2 * a * b);//AOB的cos值
            double AOB = acos(cAOB);//AOB弧度
            Point B(b * cAOB, b * sqrt(1 - cAOB * cAOB));//B点坐标
            double h = Cross(O, A, B) / c;//圆心到直线AB的距离
            if (h >= r || a * a + c * c <= b * b || b * b + c * c <= a * a)//线段AB不经过圆
            {
                ans[i] += c, ans[j] += c;
                continue;
            }
            double d1 = sqrt(a * a - r * r);
            double d2 = sqrt(b * b - r * r);//两条切线的长度
            double rad1 = acos(r / a), rad2 = acos(r / b);
            AOB -= rad1 + rad2;//扇形的角度
            double res = d1 + d2 + r * AOB;//两条切线+一段弧
            ans[i] += res, ans[j] += res;
        }
    }
    for (int i = 1; i <= m; i++)
        cout << fixed << setprecision(12) << ans[i] << endl;
    return 0;
}
posted @ 2021-09-12 22:23  Lecoww  阅读(110)  评论(0编辑  收藏  举报