Project Euler 361
Project Euler 361
题目
Subsequence of Thue-Morse sequence
The Thue-Morse sequence \(\{T_n\}\) is a binary sequence satisfying:
- \(T_0 = 0\)
- \(T_{2n} = T_n\)
- \(T_{2n+1} = 1 - T_n\)
The first several terms of \({T_n}\) are given as follows:
\(01101001{\color{red}{10010}}1101001011001101001\dots\).
We define \(\{A_n\}\) as the sorted sequence of integers such that the binary expression of each element appears as a subsequence in \(\{T_n\}\).
For example, the decimal number \(18\) is expressed as \(10010\) in binary. \(10010\) appears in \(\{T_n\}\) (\(T_8\) to \(T_{12}\)), so \(18\) is an element of \(\{A_n\}\).
The decimal number \(14\) is expressed as \(1110\) in binary. \(1110\) never appears in \(\{T_n\}\), so \(14\) is not an element of \(\{A_n\}\).
The first several terms of \(A_n\) are given as follows:
| \(n\) | \(0\) | \(1\) | \(2\) | \(3\) | \(4\) | \(5\) | \(6\) | \(7\) | \(8\) | \(9\) | \(10\) | \(11\) | \(12\) | \(\dots\) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| \(A_n\) | \(0\) | \(1\) | \(2\) | \(3\) | \(4\) | \(5\) | \(6\) | \(9\) | \(10\) | \(11\) | \(12\) | \(13\) | \(18\) | \(\dots\) |
We can also verify that \(A_{100} = 3251\) and \(A_{1000} = 80852364498\).
Find the last \(9\) digits of \(\sum_{k=1}^{18}A_{10^k}\).
解决方案
Thue–Morse 序列 \((T_n)\) 定义为\(T_n = s_2(n)\bmod 2,\)其中 \(s_2(n)\) 是 \(n\) 的二进制 1 的个数。题目中 \(A\) 是所有满足「其二进制表示作为 连续子串 出现在 \((T_n)\) 中」的非负整数按从小到大排序得到的序列,且 \(A_0=0\)。
对 \(x>0\),其标准二进制表示首位一定是 1,因此:
- 对每个长度 \(k\ge 1\),落在区间 \([2^{k-1},2^k)\) 的数恰好对应 所有以 1 开头、长度为 \(k\) 的二进制串。
- 同一长度 \(k\) 内,数值大小与二进制串的字典序一致。
- 不同长度按长度分块:所有 \(k\) 位数都小于任何 \((k+1)\) 位数。
所以问题转化为:对每个 \(k\),令 \(L(k)\) 为 Thue–Morse 中出现过的、以 1 开头的长度 \(k\) 子串的不同个数。 令\(\displaystyle{S(k)=\sum_{i=1}^k L(i)}\),则 \(A_n(n\ge1)\) 在长度分块里:找最小 \(k\) 使 \(S(k)\ge n\),并在长度 \(k\) 的那一块里取第\(o=n-S(k-1)-1\)个(从 0 开始)字典序子串,再把该子串当作二进制数。
接下来要解决三件事:
- 快速算 \(L(k)\) 与 \(S(k)\),以便从 \(n\) 找到长度 \(k\) 与 \(o\)。
- 给定 \((k,o)\),快速找到该子串在 Thue–Morse 中的一个起点位置 \(pos\)。
- 给定 \((pos,k)\),快速求该长度 \(k\) 子串对应的二进制整数 \(\bmod 10^9\),而不显式生成 \(k\) 位。
可见,Thue–Morse 也是如下 2-一致代换的不动点:\(\mu(0)=01, \mu(1)=10,\)注意这里的\(\mu\)只是一个函数名,用来表示一个替换规则。并把它作用在任意二进制串上:把串中每个字符分别替换后再拼接(例如 \(\mu(010)=\mu(0)\mu(1)\mu(0)=011001\))。从初始串 \(0\) 出发不断重复这一替换,会得到一串不断增长的前缀:
\[ 0, 01, 0110, 01101001, \dots \]
这些前缀在长度上依次翻倍,且每一步都是上一步的延长;因此它们确定了一条唯一的无限二进制序列。该无限序列的第 \(n\) 位恰好等于 Thue–Morse 序列的 \(T_n\),也就是说 Thue–Morse 就是由这条替换规则生成的。
考虑长度 \(k>4\) 的一个出现过的子串 \(w=T[pos:pos+k)\)。由于代换长度为 2,子串 \(w\) 必然来自两种“对齐方式”之一:
- 偶对齐(pos 为偶数):\(pos=2q\)。此时 \(w\) 落在 \(\mu(T[q:])\) 的块边界上。把 \(w\) 每两位分组 \((w_0w_1)(w_2w_3)\cdots\),就能通过规则\(\mu\)唯一还原出一个较短串 \(u\),使得 \(w\) 是 \(\mu(u)\) 的前 \(k\) 位。此时 \(|u|=\lceil k/2\rceil\),且首位保持不变(\(\mu(1)\) 以 1 开头)。
- 奇对齐(pos 为奇数):\(pos=2q+1\)。此时 \(w\) 从某个 \(\mu(\cdot)\) 块的第二位开始,相当于对 \(\mu(v)\) 做“左移一位再截取”。此时 \(|v|=\lfloor k/2\rfloor+1\),并且首位翻转:因为 \(\mu(0)=01\) 的第二位是 1,而 \(\mu(1)=10\) 的第二位是 0。
并且当 \(k>4\) 时,这种可对齐方式是唯一的(因为通过为\(k\)的串生成新的长度为\(2k,2k-1,2k-2\)这4类串是唯一的,短的交替串是唯一会产生二义性的情况,长度 \(\le4\) 可以直接硬处理/预处理)。
于是对“以 1 开头的长度 \(k\) 子串集合”,它由两部分构成:
- 来自偶对齐:对应“以 1 开头、长度 \(\lceil k/2\rceil\)”的子串集合,数量 \(L(\lceil k/2\rceil)\);
- 来自奇对齐:对应“以 0 开头、长度 \(\lfloor k/2\rfloor+1\)”的子串集合。由于 Thue–Morse 的因子集合对按位取反封闭(出现一个子串则其按位取反也出现),所以“以 0 开头”的数量与“以 1 开头”相同,数量为 \(L(\lfloor k/2\rfloor+1)\)。
从而得到(对 \(k>4\)):
- 若 \(k=2n:L(2n)=L(n)+L(n+1)\)
- 若 \(k=2n+1:L(2n+1)=2L(n+1)\)
初值:\(L(1)=1,L(2)=2,L(3)=3,L(4)=5.\)
因此,我们要用二分从 \(n\) 找 \(k\)。因此也需要 \(S(k)\) 的 \(O(\log k)\) 算法。对 \(k\ge 7\),用上面的 \(L\) 递推把 \(S(2n)\)、\(S(2n+1)\) 写成 \(S(n),S(n+1)\) 的线性组合(推导时需要把小的特殊项常数单独扣出来,最终得到对 \(n\ge3\) 的统一形式):
\[ \begin{aligned} S(2n) &= 3S(n) + S(n+1) - 4,\\ S(2n+1) &= S(n) + 3S(n+1) - 4, \end{aligned} \]
其中,\(n\ge 3\)。并且由\(L(1\dots6)\)给出基值求得:\(S(0)=0,S(1)=1,S(2)=3,S(3)=6,S(4)=11,S(5)=17,S(6)=25.\)
这样 \(S(k)\) 也能在 \(O(\log k)\) 时间得到,于是可以对 \(k\) 做指数扩张 + 二分,找到最小 \(k\) 使 \(S(k)\ge n\),并得到 \(o\)。
定义 \(p(k,o,\texttt{bit})\):表示 以 \(\texttt{bit}\) 开头 的所有长度 \(k\) 因子按字典序排序后,第 \(o\) 个因子的一个出现位置起点。
对 \(k\le4\) 直接预处理表即可(长度很小,枚举 Thue–Morse 前几十位就足够覆盖所有长度 \(\le4\) 因子)。
对 \(k>4\),令\(a=\left\lceil\dfrac{k}{2}\right\rceil,b=\left\lfloor\dfrac{k}{2}\right\rfloor+1.\)
那么偶对齐那部分有 \(L(a)\) 个,奇对齐那部分有 \(L(b)\) 个。关键点是 字典序里两部分的先后顺序取决于首位 \(\texttt{bit}\):
- 首位为 1 时:偶对齐那组排在前(直观上它来自 \(\mu(1)=10\) 的块边界结构),奇对齐排在后;
- 首位为 0 时:顺序相反(可由按位取反导致字典序整体反向来推出)。
同时,位置在代换层级上满足:
- 偶对齐:子串起点是偶数,映射到上一层起点乘 2;
- 奇对齐:子串起点是奇数,映射到上一层起点乘 2 再加 1,且首位翻转。
因此递推为(\(k>4\)):
若 \(\texttt{bit}=1\):
- 若 \(o < L(a):p(k,o,1)=2\cdot p(a,o,1)\)
- 否则:\(p(k,o,1)=2\cdot p(b,o-L(a),0)+1\)
若 \(\texttt{bit}=0\):
- 若 \(o < L(b):p(k,o,0)=2\cdot p(b,o,1)+1\)
- 否则:\(p(k,o,0)=2\cdot p(a,o-L(b),0)\)
最终我们需要的是 \(pos = p(k,o,1)\)。
接下来需要计算二进制子串 \(T[pos:pos+k)\) 所表示的整数。
对每个 \(h\ge 0\),考虑 Thue–Morse 序列中从位置 \(0\) 开始、长度为 \(2^h\) 的前缀块,记为\(Z_h, |Z_h|=2^h.\)由于 Thue–Morse 具有“自相似 + 取反”的结构,长度 \(2^h\) 的对齐块只可能是两种之一:
- \(Z_h\) 本身;
- \(\overline{Z_h}\):对 \(Z_h\) 逐位取反得到的块。
并且有如下对齐性质:若 \(pos\) 是 \(2^h\) 的倍数,则
\[ T[pos:pos+2^h)= \begin{cases} Z_h,& T_{pos/2^h}=0,\\ \overline{Z_h},& T_{pos/2^h}=1. \end{cases} \]
设模数 \(M=10^9\)。预处理两类量:
- 幂\(P_h = 2^{2^h}\bmod M;\)
- 块值\(V_h(0)\equiv \text{value}(Z_h)\pmod M, V_h(1)\equiv \text{value}(\overline{Z_h})\pmod M,\) 其中 \(\text{value}(\cdot)\) 表示把一个二进制串当作二进制整数来解释。
块的构造满足\(Z_{h+1}=Z_h\Vert \overline{Z_h},\)
因此得到递推: \[ \begin{aligned} P_{h+1} &\equiv P_h^2 \pmod M,\\ V_{h+1}(0) &\equiv V_h(0)\cdot P_h + V_h(1)\pmod M,\\ V_{h+1}(1) &\equiv V_h(1)\cdot P_h + V_h(0)\pmod M. \end{aligned} \]
接下来,将区间 \([pos,pos+k)\) 做标准的 dyadic 分解:从左到右,每次取一个起点对齐到 \(2^h\)(即当前 \(cur\) 可被 \(2^h\) 整除),且\(2^h\) 不超过剩余长度的最大块。
若取到的块长度为 \(2^h\),起点为 \(cur\),则它对应的块类型由\(b=T_{cur/2^h}\)决定,于是该块的模值就是 \(V_h(b)\)。
用“二进制拼接”的方式滚动更新答案:\(ans \leftarrow ans\cdot P_h + V_h(b)\pmod M.\)
dyadic 分解的块数为 \(O(\log k)\),因此整个取值过程也是 \(O(\log k)\),无需显式生成长度为 \(k\) 的子串。
代码
1 | #!/usr/bin/env python3 |