Keyboard Free (计算几何+自适应辛普森积分/等分圆)2020牛客多校第二场

探讨了一道复杂的几何题目,给出了解决三同心圆上随机取点形成三角形面积期望值的方法。通过坐标旋转、角度转换及自适应辛普森积分算法,最终实现了问题的解决。

原题题面

Given three concentric circles whose radiuses are r1r_1r1,r2r_2r2,r3r_3r3respectively, and A,B,CA,B,CA,B,C are the moving points on the given three circles respectively. Determine the expected area of △ABC\triangle ABCABC .

输入描述

The first line contains one integer T(1≤T≤1000)T(1 \leq T \leq 1000)T(1T1000), denoting the number of test cases.
For each test case:
One line containing three integers r1,r2,r3(1≤r1,r2,r3≤100)r_1, r_2, r_3(1 \leq r1,r2,r3 \leq 100)r1,r2,r3(1r1,r2,r3100), denoting the radiuses of three given concentric circles.

输出描述

Print TTT lines each containing one real number with one decimal places after the decimal point, denoting the answer to curresponding test case.
It’s guaranteed that the second decimal place after the decimal point is neither 4 nor 5.

输入数据

2
1 1 1
2 3 5

输出数据

0.5
5.5

题面解析

一道好难好难的几何题。
不妨设r1≤r2≤r3r_1\leq r_2 \leq r_3r1r2r3
首先容易考虑到的是让AAA变为定点,然后设点B,CB,CB,C在列方程求解,然后就会得到如下的二重积分:在这里插入图片描述
里面有绝对值,而且无法分类讨论(r1,r2,r3是未知的常数),故放弃该方法,采用其他策略。
可以采用如下办法。
我们让AAA作为定点,B,CB,CB,C为动点,算出C到AB的期望距离后乘以AB的长度除以2。
在这里插入图片描述
对于该图,我们可以旋转坐标系(你可以试着旋转你的头来治好颈椎病 ),让P1AP_1AP1A垂直于xxx轴。
治好你的颈椎病
我们设∠OP1G=α\angle OP_1G=\alphaOP1G=α,设C(r3cosβ,r3sinβ)C(r_3cos\beta,r_3sin\beta)C(r3cosβ,r3sinβ),那CCCABABAB的直线距离的期望是
12π∗∫02π∣sinα−cosβ∣ dβ\frac{1}{2\pi}*\int_{0}^{2\pi } {|sin\alpha-cos\beta|} \,{\rm d}\beta2π102πsinαcosβdβ.

=12π∗2∫0π∣sinα−cosβ∣ dβ\frac{1}{2\pi}*2\int_{0}^{\pi } {|sin\alpha-cos\beta|} \,{\rm d}\beta2π120πsinαcosβdβ.(这样就可以很好地处理绝对值的问题了)

=12π∗2(∫π2−απ(sinα−cosβ) dβ+∫0π2−α(cosα−sinβ) dβ)\frac{1}{2\pi}*2(\int_{\frac{\pi}{2}-\alpha}^{\pi } {(sin\alpha-cos\beta)} \,{\rm d}\beta+\int_{0}^{\frac{\pi}{2}-\alpha} {(cos\alpha-sin\beta)} \,{\rm d}\beta)2π12(2παπ(sinαcosβ)dβ+02πα(cosαsinβ)dβ).

=12π∗4r3(αsinα+cosα)\frac{1}{2\pi}*4r_3(\alpha sin\alpha+cos\alpha)2π14r3(αsinα+cosα)

CCCABABAB的直线距离的期望只和α\alphaα有关。
有了这个结论之后,我们可以设A(r1,0),B(r2cosβ,r2sinβ)A(r_1,0),B(r_2cos\beta,r_2sin\beta)A(r1,0),B(r2cosβ,r2sinβ),把α\alphaαβ\betaβ表示。
在这里插入图片描述
如图所示,我们做BD⊥xBD⊥xBDx轴,则△OAG∽△BAD△OAG∽△BADOAGBAD(AA,即两个角相等),因为∠ABD\angle ABDABD可以计算,所以OGOGOG就可以计算,进而推出α\alphaα,可以得到
sinα=r1r3∗(r2sinβ)2(r2sinβ)2+(rscosβ−r1)2sin\alpha=\frac{r_1}{r_3}*\sqrt \frac{(r_2sin\beta)^2}{(r_2sin\beta)^2+(rs_cos\beta-r_1)^2}sinα=r3r1(r2sinβ)2+(rscosβr1)2(r2sinβ)2
所以最后的答案是
12∗2π∗2π∫02π(4r3(αsinα+cosα)(r2sinβ)2+(r2cosβ−r1)2dβ\frac{1}{2*2\pi*2\pi}\int_{0}^{2\pi } {(4r_3(\alpha sin\alpha+cos\alpha)\sqrt{(r_2sin\beta)^2+(r_2cos\beta-r1)^2}}{\rm d}\beta22π2π102π(4r3(αsinα+cosα)(r2sinβ)2+(r2cosβr1)2dβ
当然,这个东西的精确值显然超出笔者范围了,于是笔者采用了自适应辛普森积分来做(其实就是把圆正等分成上千个离散点,分别代入求结果)
需要注意的是,如果采用自适应辛普森积分的话,请注意精确度的大小控制,否则会超时。

AC代码(487ms)

#include<bits/stdc++.h>
using namespace std;
const double eps=1e-4;//精确度要注意不能太小,否则超时
const double PI=acos(-1.0);
long long r1, r2, r3;
double f(double a)//积分内的式子
{
    if(a==0)
        return 0;//特殊处理,否则会RE
//    printf("a=PI/%f\n",PI/a);
    double ans1=sqrt(r2*sin(a)*r2*sin(a)+(r2*cos(a)-r1)*(r2*cos(a)-r1));
//    printf("ans1=%f\n",ans1);
    double angle1=atan((r2*sin(a))/(r2*cos(a)-r1));
    double b=asin(r1*sin(angle1)/r3);
//    printf("b=%f\n");
    double ans2=4*r3*(cos(b)+b*sin(b));
//    printf("ans2=%f\n", ans2);
    return ans1*ans2;
}
double SimpleSimpson(double a, double b)//辛普森积分
{
    return (b-a)/6.0*(f(a)+f(b)+4*f((a+b)/2.0));
}
double Simpson(double l, double r, double ans)//自适应辛普森积分
{
    double mid=(l+r)/2;
//    printf("mid:%f\n",mid);
    double Simpson_l=SimpleSimpson(l, mid);
    double Simpson_r=SimpleSimpson(mid, r);
//    printf("l:%f r:%f ans:%f\n",Simpson_l,Simpson_r,ans);
    if (fabs(Simpson_l+Simpson_r-ans)<=eps)
        return ans;
    return Simpson(l, mid, Simpson_l)+Simpson(mid, r, Simpson_r);
}
void solve1()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%lld%lld%lld", &r1, &r2, &r3);
        if (r1>r2)
            swap(r1, r2);
        if (r2>r3)
            swap(r2, r3);
        if (r1>r3)
            swap(r1, r3);
//        printf("r1:%lld r2:%lld r3:%lld\n",r1,r2,r3);
//        printf("%f %f %f\n",f(0),f(PI),f(PI/2.0));
//        printf("%f\n",SimpleSimpson(0, PI));
        printf("%.1f\n", Simpson(0, 2*PI, SimpleSimpson(0, 2*PI))/(2*PI*2*PI*2));
    }
}
int main()
{
//    ios_base::sync_with_stdio(false);
//    cin.tie(0);
//    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    long long test_index_for_debug=1;
    char acm_local_for_debug;
    while(cin>>acm_local_for_debug)
    {
        cin.putback(acm_local_for_debug);
        if (test_index_for_debug>100)
        {
            throw runtime_error("Check the stdin!!!");
        }
        auto start_clock_for_debug=clock();
        solve1();
        auto end_clock_for_debug=clock();
        cout<<"\nTest "<<test_index_for_debug<<" successful"<<endl;
        cerr<<"Test "<<test_index_for_debug++<<" Run Time: "
            <<double(end_clock_for_debug-start_clock_for_debug)/CLOCKS_PER_SEC<<"s"<<endl;
        cout<<"--------------------------------------------------"<<endl;
    }
#else
    solve1();
#endif
    return 0;
}

后记

感谢群聚XLor的指导,耽误了您太多时间真不好意思…
DrGilbert 2020.7.14

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值