Skip to main content
Original474 words2 min

题解:P3788 幽幽子吃西瓜

Summary

一道几何题,西瓜被切去一段后求正视时红色部分占可见面积的比例。设截面半圆面积为 1,角度为 x 的半圆投影面积为 sin x。图形左右对称且独立,按两切角的大小关系分类讨论每个半圆对总可见面积与红色可见面积的贡献,把整圆拆成两个半圆、后半圆用对称映射到前半圆处理,最后输出红色面积与总可见面积之比。

解题思路​

这是一道初中数学题。

由于只考虑面积比例,不妨设每个截面半圆的面积为 11。整个图形关于视线左右对称且相互独立,因此左右两半可以分别计算。

设 s(x)s(x) 表示角度为 xx 的半圆的可视面积,即投影面积:

s(x)=cos⁡(90∘−x)=sin⁡xs(x)=\cos\left(90^\circ-x\right)=\sin x

注意代码中需要将角度转为弧度。

38 Bcpp
double s(int x){return sin(x*pi/180);}

考虑一个半圆内的情况,即 0≤a,b≤1800\le a,b\le 180。左右两侧的边界分别可以表示为 aa 和 180−b180-b。

定义函数 f(a,b)f(a,b) 计算一个半圆对 SsumS_{\text{sum}} 和 SredS_{\text{red}} 的贡献,分类讨论:

  1. a≤ba\le b:表示切去中间一段。

总可见面积为:

s(min⁡(max⁡(a,180−b),90))s(\min(\max(a,180-b),90))

若 a<180−ba<180-b,红色可见,贡献为:

s(b)−s(a)s(b)-s(a)

否则红色被绿色瓜皮挡住,贡献为 00。

  1. a>ba>b:表示切去的区间跨过正面方向。

此时红色一定可见,总可见面积为:

s(min⁡(min⁡(a,180−b),90))s(\min(\min(a,180-b),90))

红色面积为:

s(b)s(b)

得到核心函数:

157 Bcpp
void f(int a,int b)
{
if(a<=b)
{
sum+=s(min(max(a,180-b),90));
if(a<180-b)red+=s(b)-s(a);
}
else
{
sum+=s(min(min(a,180-b),90));
red+=s(b);
}
}

接下来只需要把整圆按 180∘180^\circ 分成两个半圆。后半圆通过 x↦360∘−xx\mapsto 360^\circ-x 对称到前半圆处理。

若某个半圆没有被切到,则它完整可见,直接令:

Ssum←Ssum+1S_{\text{sum}}\gets S_{\text{sum}}+1

最后答案为:

SredSsum×100%\frac{S_{\text{red}}}{S_{\text{sum}}}\times 100\%

参考代码​

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

const double pi=acos(-1);
double s(int x){return sin(x*pi/180);}
double sum=0,red=0;
void f(int a,int b)
{
if(a<=b)
{
sum+=s(min(max(a,180-b),90));
if(a<180-b)red+=s(b)-s(a);
}
else
{
sum+=s(min(min(a,180-b),90));
red+=s(b);
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin>>T;
while(T--)
{
int a,b;
cin>>a>>b;
sum=red=0;
if(a<b)
{
if(a<180&&b<180){f(a,b);sum+=1;}
else if(a<180&&b>=180){f(a,180);f(360-b,180);}
else if(a>=180&&b>=180){f(360-b,360-a);sum+=1;}
}
else
{
if(a<180&&b<180)f(a,b);
if(a>=180&&b<180){f(0,b);f(0,360-a);}
else if(a>=180&&b>=180)f(360-b,360-a);
}
cout<<fixed<<setprecision(1)<<red*100/sum<<'%'<<'\n';
}
return 0;
}