暑训1-F

题意

省流:

给定球冠半顶角 , 求该球冠沿大圆扫过 时扫过的总面积。

不省流:

边界

:此时球冠为整个球。

:此时球冠占据超过半球,当 时可以覆盖整球。

Case1:小球冠

该面积可被割成三块:

alt

其中图上的直线都是球面上的直线(大圆的弧)。可以看出所求面积即为半顶角 的球冠面积 加上条带面积。显然两个半球冠不会相交。

条带面积

容易看出来这是一个完整条带的一部分,占比为 . 完整条带面积为整球减去两个半顶角 的球冠面积。

球冠面积

单位球面面积元

积分

Case2:大球冠

并且

相当于一个小”空洞“球冠在移动,此时要求起终点球冠的交面积 。答案为整球面积减

alt

实际上这些球面直线与圆并没有额外的交点。

此时有两球面三角形 ,有两个部分球盖 .

进行一个简单的面积容斥:记

代入相关公式得到

球面三角

利用面积容斥,单位球上的球面三角面积

球面余弦定理和正弦定理

球面三角形任一角的余弦等于其它两角余弦的乘积冠以负号加上这两角的正弦及其夹边余弦的连乘积。

球面三角形任一边的余弦等于其它两边余弦的乘积加上这两边的正弦及其夹角余弦的连乘积。

球面三角形三组边的正弦与角的正弦比值相等。

求 x_1 , x_2

利用边的余弦公式( , ):

这里已经是一次方程了,剩下的步骤较简单。

AC代码

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

typedef long double ld;
#define _INT(X) int (X); scanf("%d", &(X));

const ld Pi = acosl(-1);

struct dot3
{
	ld x, y, z;
	void input()	{scanf("%Lf%Lf%Lf", &x, &y, &z);}
	ld	 len()		{return sqrtl(x * x + y * y + z * z);}
	ld operator ^ (dot3 b)
	{
		return acosl((x * b.x + y * b.y + z * b.z) / (sqrtl(x * x + y * y + z * z) * b.len()));
	}
};

ld S(ld a)
{
	return 2.0 * Pi * (1.0 - cosl(a));
}

void solve()
{
	ld r, d;
	scanf("%Lf%Lf", &r, &d);
	ld a = d / r;
	dot3 A, B;
	A.input();B.input();
	ld th = A ^ B;
	
	cerr << "a " << a << '\n';
	cerr << "th " << th << '\n';
	if(a < (Pi / 2))
	{
		printf("%.18Lf\n", (r * r) * (S(a) + ((th / (2.0 * Pi)) * (4.0 * Pi - 2.0 * S((Pi / 2) - a)))));
		//S(a) + S(条带状区域)
		//th占的经线部分 * (4pi - 两个球冠)
		return;
	}
	if(a < Pi)
	{
		ld a1 = Pi - a;
		if(a1 * 2 < th)
		{
			printf("%.18Lf\n", (r * r) * (4.0 * Pi));
			return;
		}
		else
		{
			ld x1 = acosl((cosl(th) - cosl(a1) * cosl(a1)) / (sinl(a1) * sinl(a1)));
			ld x2 = acosl(tanl(th / 2.0) / tanl(a1));
			ld S1 = 2.0 * Pi - 2.0 * x1 - 4.0 * cos(a1) * x2;
			printf("%.18Lf\n", (r * r) * (4.0 * Pi - S1));
			return;
		}
	}
	printf("%.18Lf\n", (r * r) * (4.0 * Pi));
	return;
}

int main()
{
	_INT(t);
	while(t--)
		solve();
	return 0;
}