题解:P8608 [蓝桥杯 2013 国 B] 农场阳光
因为圆盘与地面平行,所以在地面的投影也是圆形,半径不变,圆心坐标的计算公式题目已经给了。
问题转化为给定矩形区域和若干圆,求矩形区域中没有被圆覆盖的面积。
多个圆重叠的部分直接计算比较困难。
注意到只需要输出两位小数,于是考虑使用自适应辛普森积分。
将
计算函数值时先求出所有圆在直线上覆盖的区间端点(如果有的话),然后按顺序扫过所有点,记录当前位置被覆盖了几次,累加被覆盖次数为
python 可以使用绘图库,非常方便。
from math import sqrt, sin, cos, pi
# import matplotlib.pyplot as plt
# import numpy as np
eps = 1e-7
a, b = map(int, input().split())
g = float(input()) / 180 * pi
n = int(input())
cirs = [tuple(map(float, input().split())) for _ in range(n)]
cotg = cos(g) / sin(g)
# x, y, r
cirs = [(x + z * cotg, y, r) for x, y, z, r, in cirs]
# fig, ax = plt.subplots()
# ax.set_xlim(0, a)
# ax.set_ylim(0, b)
# ax.set_box_aspect(b / a)
#
# for x, y, r in cirs:
# an = np.linspace(0, 2 * np.pi, 100)
# plt.plot(x + r * np.cos(an), y + r * np.sin(an))
# def showline(func):
# def wrapper(fi):
# plt.plot([fi, fi], [0, b])
# return func(fi)
#
# return wrapper
callf = 0
# @showline
def f(fi: float) -> float:
global callf
callf += 1
l = [(0, 0), (b, 0)]
for x, y, r, in cirs:
if abs(fi - x) < r:
d = sqrt(r ** 2 - (fi - x) ** 2)
if y + d > 0 and y - d < b:
l.append((y - d, 1))
l.append((y + d, -1))
l.sort()
ret: float = 0
fills: int = 0
last = l[0][0]
for p, c in l:
if fills == 0:
ret += p - last
fills += c
last = p
return ret
# print(f(1))
# plt.show()
def simpson(l: float, r: float) -> float:
mid = (l + r) / 2
return (r - l) / 6 * (f(l) + f(r) + 4 * f(mid))
def integrate(l: float, r: float, step: int) -> float:
mid = (l + r) / 2
s = simpson(l, mid) + simpson(mid, r)
if step <= 0 and abs(s - simpson(l, r)) < eps:
return s
return integrate(l, mid, step - 1) + integrate(mid, r, step - 1)
print(f'{integrate(0, a, 5):.2f}')
# print(callf)