Project Euler 246

Project Euler 246

题目

Tangents to an ellipse

A definition for an ellipse is:

Given a circle $c$ with centre $M$ and radius $r$ and a point $G$ such that $d(G,M)<r$, the locus of the points that are equidistant from $c$ and $G$ form an ellipse.

The construction of the points of the ellipse is shown below.

Given are the points $M(-2000,1500)$ and $G(8000,1500)$.

Given is also the circle $c$ with centre $M$ and radius $15000$.

The locus of the points that are equidistant from $G$ and $c$ form an ellipse $e$.

From a point $P$ outside $e$ the two tangents $t_1$ and $t_2$ to the ellipse are drawn.

Let the points where $t_1$ and $t_2$ touch the ellipse be $R$ and $S$.

For how many lattice points $P$ is angle $RPS$ greater than $45$ degrees?

解决方案

$|EG|=|EU|$,$|EU|+|EM|=r$,因此$|EG|+|EM|=r=2a$,其中$2a$是椭圆的长轴长度,焦距$2c=d(G,M)$。由于椭圆的中心也在个点,那么为了方便处理问题,将椭圆的中心挪到原点,那么可以写成椭圆的标准方程:

其中$a=7500,c=5000,b^2=a^2-c^2$。

那么,由于椭圆关于两个坐标轴对称,因此只考虑第一象限以及$x,y$轴正半轴上的格点。

对于圆外任意一点$P(x_0,y_0)$,那么该点的点斜式方程为$y-y_0=k(x-x_0)$,那么和椭圆方程联立,得到一个关于$x$的一元二次方程:

由于所求直线为切线,因此令其$\Delta=0$,得到关于$k$的一元二次方程:

也就是说,$k$不同的两个解对应着直线的斜率,不失一般性,假设其分别为$k_0,k_1$。那么这两条直线夹角的正切值为$\left|\dfrac{k_1-k_0}{1+k_0k_1}\right|$。

解决本题时,还采用了一个假设:当$\angle RPS=45°$时,$P$点的轨迹$\Gamma$是一个凸曲线。(如果$\dfrac{a}{b}$值太大,那么$\Gamma$将不会是一个凸曲线,我在这里没有进行太多的研究。)

因此,枚举所有$x_0\ge0$,二分查找$y_0$使其恰好在$\Gamma$外部。那么,$y_0$以下(不包括$y_0$)的所有点都是答案,不过,这需要排除在椭圆内部的点。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
from math import ceil, sqrt
from itertools import count

Mx, My = -2000, 1500
Gx, Gy = 8000, 1500
R = 15000
a = R / 2
c = ((Mx - Gx) ** 2 + (My - Gy) ** 2) ** 0.5 / 2
a2 = a ** 2
b2 = a2 - c ** 2
eps = 1e-10
cnt2 = cnt4 = 0
pre_r = int((a + 4) * (a + 4))

for x0 in count(0, 1):
y0 = ceil(sqrt(b2 * (1 - x0 * x0 / a2)) - eps) if x0 < a else 0
l = y0
r = pre_r
while l < r:
mid = (l + r) >> 1
if x0 != a:
A = x0 * x0 - a2
B = -2 * x0 * mid
C = mid * mid - b2
D = B * B - 4 * A * C
k = [(-B - sqrt(D)) / (2 * A), (-B + sqrt(D)) / (2 * A)]
tg = (k[1] - k[0]) / (1 + k[0] * k[1])
else:
k = (mid * mid - b2) / (2 * x0 * mid)
tg = 1 / k
if 0 < tg <= 1:
r = mid
else:
l = mid + 1
if l == 0:
break
# 统计不同点的个数。
if x0 == 0:
cnt2 += l - y0
elif y0 > 0:
cnt4 += l - y0
else:
cnt4 += l - 1
if x0 > a:
cnt2 += 1
pre_r = l
ans = cnt2 * 2 + cnt4 * 4
print(ans)

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
Ujimatsu Chiya 微信 微信
Ujimatsu Chiya 支付宝 支付宝