0456 原点三角形三
* * * *
拉格朗日计划
* * * *
原点三角形三

令 \begin{align*} x_n&=(1248^n \bmod 32323)-16161 \\ y_n&=(8421^n \bmod 30103)-15051 \\ P_n&=\{(x_1, y_1), (x_2, y_2), …, (x_n, y_n)\} \end{align*} 例如 $$P_8=\{(-14913, -6630), (-10161, 5625), (5226, 11896), (8340, -10778), (15852, -5203), (-15165, 11295), (-1427, -14495), (12407, 1060)\}.$$ 记以$P_n$中的元素为顶点且内部包含原点的三角形的总数为$C(n)$。

例如 $C(8)=20, C(600)=8950634, C(40000)=2666610948988$。

求$C(2000000)$。

本题难度:



解答

显然三个点不可能在同一个半平面内,而它们在左右/上下半平面的分布情况可以通过旋转归约为有一点左半平面,两点在右半平面的情况。

给定左半平面的一点及其和原点的连线为L,则右半平面内能与之组成符合要求的三角形的两点必然分别位于L的两侧,因此将这些点按极角排序并统计频次后遍历左半平面的点,对每个点二分查找右半平面内符合要求的点即可。

最终结果是$333333208685971546$。

注:为便于作除法,以下代码为Python 3,且代码中打印了进度信息。

import math,bisect,itertools

n=2000000

infty=10**8
p={}
a=b=1
for i in range(n):
    a=(a*1248)%32323
    b=(b*8421)%30103
    x=a-16161
    y=b-15051
    d=math.gcd(x,y)
    x//=d
    y//=d
    p[(x,y)]=p.get((x,y),0)+1

tick=len(p)*2//100
res=0
step=0 
for r in range(4):
    s,t=zip(*sorted((-infty if x==0 else -(y/x), c) for (x,y),c in p.items() if x>0 or (x==0 and y>0)))
    s=[-infty]+list(s)+[infty]
    t=[0]+list(t)+[0]
    v=[0]+list(itertools.accumulate(t))
    for (x,y),c in p.items():
        if x < 0:
            f=-(y/x)
            i=bisect.bisect_left(s,f)
            res+=v[i]*(v[-1]-v[i]-(s[i]==f)*t[i])*c
            step+=1
            if step%tick==0:
                print(step//tick,"percent completed, current sum:", res)
    if r < 3:
        p={(-y,x):c for (x,y),c in p.items()}
    else:
        print(res//2)