2015武汉大学第八届Eming杯现场赛E题题解

题目大意
   就是现在空间中给定球,平面和点的位置, 从点的位置作为点光源散发出光求球在平面上的投影面积。
大致思路
  本题并没有用到复杂的算法, 但是对于一些数学上的几何知识提出了一定的要求, 投影如果存在的话不是圆就是椭圆(可以证明), 最复杂的情况下利用几何关系求出椭圆的半长轴, 半焦距和半短轴中的任意两个即可, 椭圆面积公式高数上应该就已经讲过可以用一维的换元积分求得了。
  由于题意只保证输入的点, 球, 平面两两不相交, 所以不同的位置关系需要分为以下几种情况
情形一情形一

显然在情形一下投影面积为零, 此时球心和点光源L在平面的异侧, 很容易判断

情形二情形二

情形二下投影面积也是零, 用 dl 表示点光源到平面P的距离, dr 表示球心到平面P的距离, r表示球O的半径显然这种情形发生的条件是
dldrr 且球心与点光源L在平面的同一侧

情形三情形三

情形三下投影面积是无穷大, 用 dl 表示点光源到平面 P 的距离, dr 表示球心到平面P的距离, r 表示球 O 的半径显然这种情形发生的条件是
drr<dldr+r 且球心与点光源L在平面的同一侧

当以上三种情形都不满足时, 投影面积才需要一定的公式计算
计算投影面积时, 注意到投影可能有两种情况

情形四情形四

情形四下点光源和球心的连线和平面P垂直, 此时投影是一个圆, 很容易计算面积

情形五情形五

情形五下光源和球心的连线与平面P不垂直, 此时投影是一个椭圆, 也就是本题最难的部分, 需要利用几何关系来确定椭圆的样子

接下来对于每种情况给出数学上的判断条件和计算方式
初始化输入得到的条件:
点光源坐标 L(xl,yl,zl)
球心坐标 O(xr,yr,zr)
球的半径 r
平面 P 的方程为 ax+by+cz+d=0
设光源到平面的距离为 dl , 球心到平面的距离为 dr
利用点到平面的距离公式可知
dl=axl+byl+czl+da2+b2+c2 dr=axr+byr+czr+da2+b2+c2

先判断情形一:
根据空间中两个点是否在平面同一侧的判定方法
如果 axl+byl+czl+d axr+byr+czr+d 正负性相同则说明两个点在同一侧, 相反则在异侧 (本题输入保证了这两个不会是0)

判断情形二:
按照点到平面的距离公式计算出了 dl dr 之后判断 dldrr 是否成立即可

判断情形三:
drr<dldr+r 是否成立即可

当以上三种都不满足时才考虑情形四和情形五

判断情形四:
圆的情形

考虑向量 LO 和平面的法向量 n 平行
其中 LO={xrxl,yryl,zrzl},n={a,b,c}
判断两个向量是否平行最简单的写法当然是叉积为0了(也可以用别的方法来判, 这里为了方便起见我直接用叉积了)…用 ijk 分别表示坐标轴三个方向的单位向量
LO // n LO×n=0 所以有
LO×n=

ixrxlajyrylbkzrzlc
=((yryl)c(zrzl)b)i((xrxl)c(zrzl)a)j+((xrxl)b(yryl)a)k=0
也就是当 (yryl)c(zrzl)b=0(xrxl)c(zrzl)a=0(xrxl)b(yryl)a=0 时两个向量平行, 此时 OL=LSOS=dldr 利用 LONLBS 可得 ONBS=LNLS
由勾股定理有 LN=OL2ON2 可以解得 BS=dlr(dldr)2r2 BS 刚好是阴影部分的半径所以这种情形下阴影部分面积为 Scircle_shadow_area=πd2lr2(dldr)2r2

判断情形五:
椭圆的情形

这一情形是所有情形中最复杂的, 首先可以发现以点 L 作为顶点, 和球O相切的所有光线形状其实就是一个圆锥的形状, 而一个这样的一个高为无穷的圆锥用一个平面斜着切一下, 切面一定是椭圆, 如图所示, 以 L,S,T 所在平面的截图如图所示, 在这样一个圆锥用平面横切的图中可以放入两个球(如图中虚线所示)对于切面边缘的点 A,B , 利用圆的切线性质 AF=AM,AF=AJ,BK=BF,BN=BF MJ=NK 这正是因为F和F’是椭圆的两个焦点的缘故, 具体圆锥上切一刀得到圆锥曲线的证明可以参见
Dandelin双球证明法
wiki: Dandelin Spheres
百度百科:Dandelin
知乎:怎么证明圆锥的截痕是椭圆、双曲线?

当前的已知条件:
LS=dl,OT=dr,OM=ON=r,
LO=(xlxr)2+(ylyr)2+(zlzr)2
d=LO , 设圆 O 的半径是 R , 接下来的 d 表示的都是 LO

LSIOTI 可得
LSOT=LIOI LIOI=LO+OIOI=LOOI+1
解得 OI=drddldr LI=LO+OI=dlddldr
LONLON LOLO=ONON=rR
所以 LOLO+OO=rR 得到 OO=(Rr)dr
那么 OI=OIOO=drdldrdRrrd
IOFIOT IOIO=OFOT
drdldrdRrrddrdldrd=Rdr , 解得 R=rdlr+dldr
那么由勾股定理 LN=OL2R2=Rd2r2r
假设椭圆的三个参数分别为 ae,be,ce (半长轴, 半短轴, 半焦距)
那么按照椭圆的性质 AF=aece,AB=2ae
由圆的切线性质 AM=AF,BN=BF , 所以 LAB 的周长
CLAB=4ae+2LN
LAB 的面积 SLAB=CLAB×R2=2aeR+R2d2r2r
而从另外一个角度 SLAB=LS×BS2=dlae
联立两个面积表达式可得 ae=R2d2r2(dl2R)r
接下来为了计算方便,使用当一个平面截圆锥得到圆锥曲线时的结论:
椭圆的离心率等于截面和圆锥的轴的交角的余弦 cosβ 与圆锥的母线和轴所成的角的余弦 cosα 之比
在本题中 β=LIS,α=MLO
cosα=LMLO=d2r2d
cosβ=ISIL=IL2LS2IL=1(dldr)2d2
那么椭圆离心率 e=cosβcosα=d2(dldr)2d2r2
由于 e=ceae
那么椭圆的面积 Seclipse_shadow_area=πaebe=πa2e(1e2)
带入 ae e 之后最终得到椭圆面积的表达式如下, 化简过程较长这里就略去了…当然写代码的话完全没有必要化简, 毕竟 e,ae 都已经临时存储了
Seclipse_shadow_area=πr2d2l(dldr)2r2d2r2(dldr)2r2
不难发现和情形四相比椭圆的面积就是相同距离的 dl dr 下圆的面积比上椭圆的离心率, 毕竟数学之美(>_<), 其实短半轴的长度就是 dl dr 相同下的情形四的圆的半径(大家可以自己证明, 利用这个可以不用到那个离心率的结论就将此题解出)

本题标程
/*
 * Author: Gatevin
 * Created Time:  2015/3/7 16:09:35
 * File Name: NoGameNoLife.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;

int Cases, A, B, C, D, xl, yl, zl, xr, yr, zr, r;
double dl, dr, d;
const double PI = acos(-1.0);

int sgn(double del)
{
    return del < -eps ? -1 : (del > eps ? 1 : 0);
}

int main()
{
    //freopen("NoGameNoLife.in", "r", stdin);
    //freopen("NoGameNoLife.out", "w", stdout);
    scanf("%d", &Cases);
    while(Cases--)
    {
        scanf("%d %d %d %d", &A, &B, &C, &D);
        scanf("%d %d %d", &xl, &yl, &zl);
        scanf("%d %d %d %d", &xr, &yr, &zr, &r);

        //情形一
        int sgnl = A*xl + B*yl + C*zl + D > 0 ? 1 : -1;
        int sgnr = A*xr + B*yr + C*zr + D > 0 ? 1 : -1;
        if(sgnl*sgnr == -1)
        {
            printf("0.00000\n");
            continue;
        }

        //情形二
        dl = abs(A*xl + B*yl + C*zl + D)*1./sqrt(A*A*1. + B*B + C*C);
        dr = abs(A*xr + B*yr + C*zr + D)*1./sqrt(A*A*1. + B*B + C*C);
        if(sgn(dr - r - dl) != -1)
        {
            printf("0.00000\n");
            continue;
        }

        //情形三
        if(dr - r < dl && sgn(dl - dr - r) != 1)
        {
            printf("Kuhaku is undefeated\n");
            continue;
        }

        //情形四
        if((yr - yl)*C == (zr - zl)*B && (xr - xl)*C == (zr - zl)*A && (xr - xl)*B == (yr - yl)*A)
        {
            double S = PI*dl*1.*dl*r*r*1./((dl - dr)*(dl - dr)*1. - r*r*1.);
            printf("%.5f\n", S);
            continue;
        }

        //情形五
        d = sqrt((xr - xl)*(xr - xl)*1. + (yr - yl)*(yr - yl)*1. + (zr - zl)*(zr - zl)*1.);
        double S = PI*dl*1.*dl*r*r*1./((dl - dr)*(dl - dr)*1. - r*r*1.);
        S *= sqrt((d*d*1. - r*r)/((dl*1. - dr)*(dl*1. - dr) - r*r*1.));
        printf("%.5f\n", S);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值