0425 素数连接
* * * *
拉格朗日计划
* * * *
素数连接

若两个正整数A、B的位数相同且只有一个数位上数字不同,或者在其中一方的左侧添加一个数字就可以得到另一方,就称这两者是相连的,记作$A\leftrightarrow B$。

若能通过一系列由不超过素数P的素数组成的连接从2到达P,就称2和P是相关联的。例如,2与127是相关联的,因为有 $$2 \leftrightarrow 3 \leftrightarrow 13 \leftrightarrow 113 \leftrightarrow 103 \leftrightarrow 107 \leftrightarrow 127.$$ 同理可以验证11和103与不是相关联的。记F(N)为不超过N且与2\textbf{不相关联}的的素数之和。

可以验证$F(10^3)=431, F(10^4)=78728$。求$F(10^7)$。

本题难度:



解答

先用筛法找出不超过$10^7$的素数,从小到大依次遍历这些素数,对每个素数,生成不超过该数且与其相连的其他素数,用并查集标记和更新连接关系即可。

生成相连的其他素数时共有在最左侧添加一位,去掉最高位,以及替换一个数字三种情况,其中去掉最高位时还需注意排除次高位为0的情况(例如103不能直接去掉最高位变成3)。

以下代码中打印了进度信息,最终结果是$46479497324$。

target=10**7
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]
primeClass={p:p for p in primeList}

print("primes generated,start computing...")
tick=len(primeList)//100

def find(x):
    if primeClass[x]==x:
        return x
    primeClass[x]=find(primeClass[x])
    return primeClass[x]

def merge(x,y):
    primeClass[find(x)]=find(y)

res=0 
for k,p in enumerate(primeList):
    s=str(p)
    for q in [int(j+s) for j in "123456789"]+[int(s[:i]+j+s[i+1:]) for i in range(len(s)) for j in "0123456789"]:
        if q < p and abs(len(str(q))-len(s)) < 2 and q in primeClass:
            merge(p,q)
    if find(p)!=find(2):
        res+=p
    if k%tick==0:
        print k//tick,"completed, current sum:", res

print res