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

原题题面

Given three concentric circles whose radiuses are r 1 r_1 r1, r 2 r_2 r2, r 3 r_3 r3respectively, and A , B , C A,B,C A,B,C are the moving points on the given three circles respectively. Determine the expected area of △ A B C \triangle ABC ABC .

输入描述

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 r 1 , r 2 , r 3 ( 1 ≤ r 1 , r 2 , r 3 ≤ 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 T T T 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

题面解析

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

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

= 1 2 π ∗ 2 ( ∫ π 2 − α π ( s i n α − c o s β )   d β + ∫ 0 π 2 − α ( c o s α − s i n β )   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β).

= 1 2 π ∗ 4 r 3 ( α s i n α + c o s α ) \frac{1}{2\pi}*4r_3(\alpha sin\alpha+cos\alpha) 2π14r3(αsinα+cosα)

C C C A B AB AB的直线距离的期望只和 α \alpha α有关。
有了这个结论之后,我们可以设 A ( r 1 , 0 ) , B ( r 2 c o s β , r 2 s i n β ) A(r_1,0),B(r_2cos\beta,r_2sin\beta) A(r1,0),B(r2cosβ,r2sinβ),把 α \alpha α β \beta β表示。
在这里插入图片描述
如图所示,我们做 B D ⊥ x BD⊥x BDx轴,则 △ O A G ∽ △ B A D △OAG∽△BAD OAGBAD(AA,即两个角相等),因为 ∠ A B D \angle ABD ABD可以计算,所以 O G OG OG就可以计算,进而推出 α \alpha α,可以得到
s i n α = r 1 r 3 ∗ ( r 2 s i n β ) 2 ( r 2 s i n β ) 2 + ( r s c o s β − r 1 ) 2 sin\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
所以最后的答案是
1 2 ∗ 2 π ∗ 2 π ∫ 0 2 π ( 4 r 3 ( α s i n α + c o s α ) ( r 2 s i n β ) 2 + ( r 2 c o s β − r 1 ) 2 d β \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}\beta 22π2π102π(4r3(αsinα+cosα)(r2sinβ)2+(r2cosβr1)2 dβ
当然,这个东西的精确值显然超出笔者范围了,于是笔者采用了自适应辛普森积分来做(其实就是把圆正等分成上千个离散点,分别代入求结果)
需要注意的是,如果采用自适应辛普森积分的话,请注意精确度的大小控制,否则会超时。

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

  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值