0400 斐波那契树
* * * *
拉格朗日计划
* * * *
斐波那契树

斐波那契树是一种递归定义的二叉树:

$T(0)$是空树。

$T(1)$是只有一个结点的树。

$T(k)$有一个根结点,其左右子树分别为$T(k-1)$和$T(k-2)$。

两名玩家分别在这棵树上取结点,每轮中一名玩家选择一个结点,并取走以该结点为根结点的子树。

最终取走整棵树的根结点的玩家负。

以下是当k分别取1到6时,先手玩家面对$T(k)$时第一步所有的必胜取法:



记$f(k)$是先手玩家面对$T(k)$时第一步所有的必胜取法的总数。例如$f(5)=1, f(10)=17$。

求$f(10000)$的最后18位数。

本题难度:



解答

本题即斐波那契树上的删边游戏(称为Green Hackenbush),这与第301题及之后一系列取石头游戏一样,是一种Nim游戏。

用所谓的Colon原则,不断把子树替换为其左右子树Nim值的异或和,即可递归计算结果为$438505383468410633$。

注:为方便书写,代码中用到walrus算符等新特性,因此以下源码为Python 3。此外,本题需要数分钟运行,但并未输出进度。

m=10**18
n=10000  

def next_counts(x,y,a,b):
    z=[0]*(n+1)
    z[a+1]=z[b+1]=1
    for c in range(n+1):
        if (d:=(b+1)^(c+1))<=n:
            z[d]=(z[d]+x[c])%m
        if (d:=(a+1)^(c+1))<=n:
            z[d]=(z[d]+y[c])%m
    return z
        
a,b=0,1
x,y=[0]*(n+1),[0]*(n+1)
y[0]=1

for i in range(3,n+1):
    x,y=y,next_counts(x,y,a,b)
    a,b=b,(a+1)^(b+1)

print(y[0])