3295. 星际旅行(计算几何)
题意:n维空间中存在一个超维球,求2点不通过圆(可以经过球表面)的最短距离
解:两点确定一条直线,3点确定一个平面,有了球心和另外两点就可以确定出一个2维平面,直接以球心为原点,其中一个点为X轴上一点建2维坐标系(这个点的坐标为(它和球心的距离, 0))。之后就是求线段和圆的位置关系了。
↓直接从这里借(tou)的图↓
建完系后得到三点,圆心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;
}