2021牛客暑期多校训练营2 F. Girlfriend(阿波罗尼斯圆/计算几何)

链接:https://ac.nowcoder.com/acm/contest/11253/F
来源:牛客网

题目描述

ZYT and ZFeT are great PUAs and both have two girlfriends,but they never think that their girlfriends could be so great at magic.

One day, when ZYT and ZFeT were busy training PU skills,they suddenly found themselves spelled by their girlfriends.

And their girlfriends told them that they would be freed after 1010010100 years .

After examining carefully , they found the mechanism of the spell:

  1. ZYT must keep the distance between himself and his lover A no less than k1k1 times the distance between himself and his lover B.
  2. ZFeT must keep the distance between himself and his lover C no less than k2k2 times the distance between himself and his lover D.

Let P1,P2P1,P2 denote the positions of ZYT and ZFeT, so that is:

∣AP1∣≥k1∣BP1∣,∣CP2∣≥k2∣DP2∣∣AP1∣≥k1∣BP1∣,∣CP2∣≥k2∣DP2∣

As they immediately discovered:their available living places formed two geometric forms in space and they may has intersection inside.
Surely the intersection was far too enough for them to become more and more gay(I mean,happy) in these 1010010100 years.
Wondered how gay they could be,they wanted to ask you the volume of of the intersection.

输入描述:

The first line of input contains a single integer TT — the number of test cases.

Each test case consists of five lines:

The first four lines contains three integers (xi,yi,zi)(xi,yi,zi), denoting the coordinates of points A,B,C,D.

Then another line contains two integers k1,k2k1,k2, described as above.

输出描述:

Output TT lines denoting the answer of each test case, answer with an error less than 10−310−3 (relatively or absolutely) will be accepted.

示例1

输入

复制

1
1 0 0
3 0 0
2 0 0
4 0 0
3 3

输出

复制

0.262

由高中数学知识,两个活动的范围显然都是”阿波罗尼斯球“(即三维空间的阿波罗尼斯圆),因此相当于求出来这两个球然后求球的相交部分的体积。关于阿波罗尼斯圆可以参考https://max.book118.com/html/2019/0517/5043002334002034.shtm,容易求得圆的半径(也就是球的半径)以及球心到其中一个端点的距离,这样由向量公式很容易就把球心确定出来了,直接套板子即可。

注意,不要忘记特判两球不交以及一个球在另一个球内的情况!

#include <bits/stdc++.h>
using namespace std;
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
const double PI = acos(-1.0);
typedef struct point {
    double x,y,z;
    point() {
    }
    point(double a, double b,double c) {
        x = a;
        y = b;
        z = c;
    }
    point operator -(const point &b)const {     //返回减去后的新点
        return point(x - b.x, y - b.y,z-b.z);
    }
    point operator +(const point &b)const {     //返回加上后的新点
        return point(x + b.x, y + b.y,z+b.z);
    }
    //数乘计算
    point operator *(const double &k)const {    //返回相乘后的新点
        return point(x * k, y * k,z*k);
    }
    point operator /(const double &k)const {    //返回相除后的新点
        return point(x / k, y / k,z/k);
    }
    double operator *(const point &b)const {    //点乘
        return x*b.x + y*b.y+z*b.z;
    }
}point;
 
double dist(point p1, point p2) {       //返回平面上两点距离
    return sqrt((p1 - p2)*(p1 - p2));
}
typedef struct sphere {//球
    double r;
    point centre;
}sphere;
void SphereInterVS(sphere a, sphere b,double &v,double &s) {
    double d = dist(a.centre, b.centre);//球心距

  
    double l1 = ((a.r*a.r - b.r*b.r) / d + d) / 2;
    double l2 = d - l1;
    double x1 = a.r - l1, x2 = b.r - l2;//分别为两个球缺的高度
    double v1 = PI*x1*x1*(a.r - x1 / 3);//相交部分r1圆所对应的球缺部分体积
    double v2 = PI*x2*x2*(b.r - x2 / 3);//相交部分r2圆所对应的球缺部分体积
     v = v1 + v2;//相交部分体积
    double s1 = PI*a.r*x1;  //r1对应球冠表面积
    double s2 = PI*a.r*x2;  //r2对应球冠表面积
    s = 4 * PI*(a.r*a.r + b.r*b.r) - s1 - s2;//剩余部分表面积
}
int t, n;
double x, y, z, r;
int cas = 1;
double xa, ya, za, xb, yb, zb, xc, yc ,zc, xd, yd, zd;
double k1, k2;
double calcdis(double x1, double y1, double z1, double x2, double y2, double z2) {
	return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) + (z1 - z2) * (z1 - z2));
}
double calcr(double x1, double y1, double z1, double x2, double y2, double z2, double k) {
	double dis = calcdis(x1, y1, z1, x2, y2, z2);
	return dis * k / (k * k - 1.0);
}
double calcp(double x1, double y1, double z1, double x2, double y2, double z2, double k) {//圆心到较远的点的距离
	double dis = calcdis(x1, y1, z1, x2, y2, z2);
	return dis + dis / (k * k - 1);
}
point calccneter(double x1, double y1, double z1, double x2, double y2, double z2, double k) {
	double dis1 = calcdis(x1, y1, z1, x2, y2, z2);
	double dis2 = calcp(x1, y1, z1, x2, y2, z2, k);
	double xx, yy, zz;
	xx = dis2 / dis1 * (x2 - x1) + x1;
	yy = dis2 / dis1 * (y2 - y1) + y1;
	zz = dis2 / dis1 * (z2 - z1) + z1;
	point ret;
	ret.x = xx, ret.y = yy, ret.z = zz;
	return ret;
}
int main() {
	int t;
	cin >> t;
	while(t--) {
		cin >> xa >> ya >> za;
		cin >> xb >> yb >> zb;
		cin >> xc >> yc >> zc;
		cin >> xd >> yd >> zd;
		cin >> k1 >> k2;
		sphere s1, s2;
		s1.r = calcr(xa, ya, za, xb, yb, zb, k1);
		s1.centre = calccneter(xa, ya, za, xb, yb, zb, k1);
		s2.r = calcr(xc, yc, zc, xd, yd, zd, k2);
		s2.centre = calccneter(xc, yc, zc, xd, yd, zd, k2);
		double v1 = 4.0 / 3 * PI * s1.r * s1.r * s1.r;
		double v2 = 4.0 / 3 * PI * s2.r * s2.r * s2.r;
		double v = 0, ss = 0;
        SphereInterVS(s1, s2, v, ss); //相交部分
        v = min(v, min(v1, v2));
        cout << fixed << setprecision(6);
        if(dist(s1.centre, s2.centre) > s1.r + s2.r) cout << 0.0 << endl;
        else if(dist(s1.centre, s2.centre) + min(s1.r, s2.r) <= max(s1.r, s2.r)) cout << min(v1, v2) << endl;//!!!很重要 一定不要忘记判断
        else cout << v << endl;
	}
	return 0;
}
原文地址:https://www.cnblogs.com/lipoicyclic/p/15031122.html