0245 可约度
* * * *
拉格朗日计划
* * * *
可约度

给定分母d,一共有$d-1$个真分数(分子小于分母的正分数),定义d的不可约度$R(d)$为这些真分数中不可约分数(分子分母互素)的比例,例如$R(12)=4/11$。

记$\varphi(d)$为欧拉Totient函数,定义d的可约度为 $$C(d)=\frac{d-\varphi(d)}{d-1},$$ 显然,若p是素数,则$C(p)=1/(p-1)$是单位分数(分子为1的既约分数)。

有些合数的可约度也是单位分数,求不超过$2\times 10^{11}$的这样的合数之和。

本题难度:



解答

若$C(d)$是单位分数,则 $$m=\frac{1}{C(d)}=\frac{d-1}{d-\varphi(d)},$$ 是整数。设 $$d=p_1^{a_1}\ldots p_k^{a_k},$$ 是d的质因数分解,则 $$\varphi(d)=p_1^{a_1-1}\ldots p_k^{a_k-1}(p_1-1)\ldots(p_k-1).$$ 因此若p是一素数且$p^2$能整除$d$,则将有p能整除$d-\varphi(d)$,但显然p不能整除$d-1$,因此d只能是无平方因子数。

d至少有两个素因子,因此$\varphi(d)$是偶数,从而d也只能是奇数(否则若d是偶数,则$d-1$奇而$d-\varphi(d)$偶,与m是整数矛盾)。

接下里有$d-\varphi(d)$是奇数,但$d-1$是偶数,因此m也是偶数。

设p是d最大的素因子,并记$x=d/p$, $y=\varphi(x)$,则有 $$m=\frac{xp-1}{xp-y(p-1)}=\frac{xp-1}{(x-y)p+y}=\frac{x}{x-y}-\frac{xy+1}{x-y}\cdot\frac{1}{(x-y)p+y},$$ 是关于p的增函数。p分别取x的最大素因子和$2\times 10^{11}/x$就可得m的上下界,枚举这一范围内的偶数m(这一范围较小,计算也比较直接),再考察 $$p=\frac{my+1}{x-m(x-y)},$$ 是否是素数就可得符合要求的d。

在实现上,先用筛法生成$\sqrt{2\times 10^{11}}+1$以内的所有素数,以便作素性检验。

接下来将这些素数放入一个栈,用深度优先搜索生成无平方因子数入栈,同时用上文的方法枚举m并找出d。

栈中的元素取$(x,\varphi(x), k)$的形式,其中k表示当前无平方因子数x的最大素因子是第k个素数$p_k$,按这一方式,$xp_{k+1},xp_{k+2},\ldots$就是接下来要入栈的无平方因子数。

最后,在入栈时可以检验$xp_{k+1}^2$是否小于$2\times 10^{11}$,这是因为$xp_{k+1}$是否符合要求已经在本次循环中检验,而若$xp_{k+1}^2$已经大于$2\times 10^{11}$,则下一次在检验$xp_{k+1}$所对应的m的范围将是空表,因此不必入栈,这样的预优化可以显著减少时间和空间的复杂度。

最终结果是$288084712410001$。

注:本题运算量较大,以下代码中打印了进度信息。

target=447214
d=[0]*target
n=2
while n < target:
    for i in range(n*n,target,n):
        d[i]=d[i]+1
    n=n+1
    while n < target and d[n]>0:
        n=n+1

primeList=[k for k in range(2,target) if d[k]==0]
primeSet=set(primeList)

def isPrime(x):
    if x < target:
        return x in primeSet
    else:
        i=0
        while i < len(primeList) and x%primeList[i]>0:
            i+=1
        return i==len(primeList)

print "prime list generated with length", len(primeList)

N=200000000000
r=0
q=[(primeList[i],primeList[i]-1,i) for i in range(1,len(primeList))]
i=0
while q:
    x,y,j=q.pop()
    p=primeList[j]
    if x*p < N:
        a=(x*p-1)/((x-y)*p+y)
        b=x*(N-1)/((x-y)*N+y*x)
        for m in range(a-a%2,b+1,2):
            if (1+m*y)%(x-m*x+m*y)==0:
                t=(1+m*y)/(x-m*x+m*y)
                z=x*t
                if t>p and z < N and isPrime(t):
                    r+=z
                    i+=1
                    print "find",z,",",i,"found in total"
        k=j+1
        while k < len(primeList)-1 and x*primeList[k]*primeList[k+1]<=N:
            q.append((x*primeList[k],y*(primeList[k]-1),k))
            k+=1
print r