0397 抛物三角形
* * * *
拉格朗日计划
* * * *
抛物三角形

在抛物线$y=x^2/k$上选择三个点$A(a, a^2/k), B(b, b^2/k), C(c, c^2/k)$。

考虑满足$1\le k\le K$、$-X\le a < b < c\le X$,且三角形ABC中至少有一个角为45度的四元整数组$(k, a, b, c)$,记这样的四元整数组的总数为$F(K, X)$。

可以验证$F(1,10)=41, F(10,100)=12492$。

求$F(10^6, 10^9)$。

本题难度:



解答

固定k,统计在每一个点处能取到45度角的三角形的个数,再减去在该点处能取到90度角的等腰直角三角形的个数,就是固定该k值时满足要求的四元整数组的总数。

用余弦定理可以计算出两种情况的a,b,c(a处作为顶点)都可以表示为$a,x-a,y-a$的形式,不过前者x可以取$k(y-k)/(y+k)$或$k(y+k)/(k-y)$,且a的取值取决于$xy+k^2$的符号或者在区间$[x-a,y-a]$以外或者在其内,此时y是$2k^2$的约数。而后则$x=-k^2/y$,a则可以取$(ky-x^2)/(2(k\pm x))$,且此时x,y都是$k^2$的约数。

因此先用筛法找出$k^2$和$2k^2$的约数列表,再根据上面的结论统计范围内吗满足要求的三元组,最后汇总即得结果$141630459461893728$。

注:为便于整除,以下代码为Pyhthon 3,且代码中打印了进度信息。此外,本题中的约数列表的生成是先维护每个数的最小素因子再递归计算,先可以先维护最大素因子(见第407题)。

target=1000001
bound=1000000000
minP=[0]*target
tick=10000

f=lambda x,y,p,q:0 if q==0 or p%q else (p//q)*2!=x and (p//q)*2!=y and max(abs(p//q),abs(x-p//q),abs(y-p//q))<=bound
g=lambda k,x,y:0 if x>=y else (max(min(min(bound,x+bound),(y-1)//2)-max(max(-bound,y-bound),x//2+1)+1,0) if k*k+x*y<0 else max(min(bound,x+bound)-max(-bound,y-bound)+1,0)-max(min(min(bound,x+bound),y//2)-max(max(-bound,y-bound),(x+1)//2)+1,0))

for i in range(2,target):
    if not minP[i]:
        for j in range(i,target,i):
            if not minP[j]:
                minP[j]=i
ans=0
for k in range(1,target):
    d=[1]
    x=k
    while x>1:
        j,p=0,minP[x]
        while x%p==0:
            x//=p
            j+=1
        t=len(d)
        for i in range(t):
            q=1
            for h in range(2*j):
                q*=p
                d.append(d[i]*q)
    ans-=sum(f(d[i],-k*k//d[i],-(k*k//d[i])*k-d[i]*d[i],2*(k-d[i]))+f(d[i],-k*k//d[i],-(k*k//d[i])*k+d[i]*d[i],2*(k+d[i])) for i in range(len(d)))
    t=len(d)
    for i in range(t):
        if (k*k//d[i])%2:
            d.append(2*d[i])
    t=len(d)
    for i in range(t):
        d.append(-d[i])
    ans+=sum(g(k,k*(i-2*k)//i,i-k)+g(k,k*(i+2*k)//(-i),i+k) for i in d) 
    if k%tick==0:
        print(k//tick,"percent completed")

print(ans)