0177 整数角四边形
* * * *
拉格朗日计划
* * * *
整数角四边形

ABCD是一个凸四边形,对角线为AC和BD。在四边形的每个顶点处,对角线和两相邻边各构成一个角,如此总共可得八个这样的角。



例如,在顶点A的两个角分别为角$\angle CAD$和角$\angle CAB$。

若一个四边形中这八个角的角度均为整数,则称其为整数角四边形。例如正方形就是整数角四边形,它的八个角都是$45^{\circ}$。

另一个(不那么显然的)例子是$\angle DAC=20^{\circ}$, $\angle BAC = 60^{\circ}$,$\angle ABD = 50^{\circ}$,$\angle CBD = 30^{\circ}$,$\angle BCA = 40^{\circ}$,$\angle DCA = 30^{\circ}$,$\angle CDB = 80^{\circ}$,$\angle ADB = 50^{\circ}$。

若把能通过旋转和/或翻转得到的四边形视为同一个,那么共有多少个整数角四边形?

注:在计算中若所得浮点数与一个整数值的误差在$10^{-9}$以内就认为它是整数。

本题难度:



解答

可以不失一般性地假设底边$AB=1$,且$\angle DAC$是八个角中最小的。

从左往右枚举底部四个角$\angle DAC$, $\angle CAB$, $\angle ABD$, $\angle DBC$的值,将其依次记作$a,b,c,d$。

从右往左把顶部四个角$\angle BCA$, $\angle DCA$, $\angle BDC$, $\angle ADB$依次标记为$e,f,g,h$。

若$a,b,c,d$都是整数,那么显然$e=180^{\circ}-b-c-d$和$h=180^{\circ}-a-b-c$也都是整数,因此只需计算并找出f和g也都是整数的情况即可。

用正弦定理和余弦定理(以及$|AB|=1$)易得 \begin{align*} |AD|&=\frac{\sin(c)}{\sin(a+b+c)} \\ |AC|&=\frac{\sin(c+d)}{\sin(b+c+d)} \\ |CD|&=\sqrt{|AD|^2+|AC|^2-2|AD|\cdot|AC|\cos a} \\ |BC|&=\frac{\sin b}{\sin(b+c+d)} \\ |BD|&=\frac{\sin(a+b)}{\sin(a+b+c)} \\ \cos f&=\frac{|CD|^2+|AC|^2-|AD|^2}{(2|CD|\cdot|AC|} \\ \cos g&=\frac{|CD|^2+|BD|^2-|BC|^2}{2|CD|\cdot|BD|} \end{align*} 注意f和g需要用反余弦计算,若用反正弦将无法区分锐角和钝角。

枚举时还应注意a是八个角中最小的,且所有八个角的和为$360^{\circ}$。

旋转和翻转最多能生成八组角度的排列(旋转四种乘以翻转两种),我们把这八种情况底部四个角从左到右形成的四元组排序,取其中最小的一组作为键值来去重。

结果是$129325$。

注: 本题在实现上有很多容易出错的细节,我花了两三天时间才完全调试正确。主要的难点有:1)需要合理选择变量,使之既能压缩枚举量,又不遗漏不重复,同时又便于判定整数角。2) 答案严格依赖于题面中所给出的精度范围。

import math
epsilon=10**(-9)
sin=[math.sin(math.radians(i)) for i in range(181)]
cos=[math.cos(math.radians(i)) for i in range(181)]

r=set()
for a in range(1,46):
    for b in range(a,181-3*a):
        for c in range(a,181-2*a-b):
            for d in range (a,181-a-b-c):
                AD=1.0*sin[c]/sin[a+b+c]
                AC=1.0*sin[c+d]/sin[b+c+d]
                CD=math.sqrt(AD*AD+AC*AC-2*AD*AC*cos[a])
                BC=1.0*sin[b]/sin[b+c+d]
                BD=1.0*sin[a+b]/sin[a+b+c]
                x,y=math.degrees(math.acos((CD*CD+AC*AC-AD*AD)/(2*CD*AC))),math.degrees(math.acos((CD*CD+BD*BD-BC*BC)/(2*CD*BD)))
                DCA,BDC=int(round(x)),int(round(y))
                #if a==1 and b==2 and c==90 and d==1: print x,y,DCA,BDC,abs(x-DCA)<=epsilon,abs(y-BDC)<=epsilon,DCA>=a,BDC>=a,180-b-c-d>=a,180-a-b-c>=a
                if abs(x-DCA)<=epsilon and abs(y-BDC)<=epsilon and DCA>=a and BDC>=a:
                    e,f,g,h=180-b-c-d,DCA,BDC,180-a-b-c
                    if a+b+c+d+e+f+g+h==360 and min(a,b,c,d,e,f,g,h)==a:
                        r.add(min((a,b,c,d),(c,d,e,f),(e,f,g,h),(g,h,a,b),(d,c,b,a),(f,e,d,c),(h,g,f,e),(b,a,h,g)))
                    
print len(r)