Project Euler 548
Project Euler 548
题目
Gozinta Chains
A gozinta chain for \(n\) is a sequence \(\{1,a,b,\dots,n\}\) where each element properly divides the next.
There are eight gozinta chains for \(12:\)
\(\{1,12\} ,\{1,2,12\}, \{1,2,4,12\}, \{1,2,6,12\}, \{1,3,12\}, \{1,3,6,12\}, \{1,4,12\}\) and \(\{1,6,12\}\).
Let \(g(n)\) be the number of gozinta chains for \(n\), so \(g(12)=8\). \(g(48)=48\) and \(g(120)=132\).
Find the sum of the numbers \(n\) not exceeding \(10^{16}\) for which \(g(n)=n\).
解决方案
令\(N=10^{16}\)。对 \(n>1\),任意一条以 \(n\) 结尾的链 \({1,\dots,d,n}\),其倒数第二项 \(d\) 必为 \(n\) 的真因子(\(d\mid n,\ d<n\))。去掉最后的 \(n\) 后,就得到一条以 \(d\) 结尾的 gozinta chain。
因此对 \(n>1\) 有递推:
\[ g(n)=\sum_{\substack{d\mid n\ d<n}} g(d), g(1)=1. \]
例如 \(n\) 为质数时只有 \({1,n}\) 一条链,所以 \(g(p)=1\)。
设\(n\)的分解为\(\displaystyle{n=\prod_{i=1}^k p_i^{e_i}.}\)此外,我们定义\(n\)的指数多重集\(\mathrm{sig}(n)\)表示\((e_1,e_2,\dots,e_k)\)按非增序排列得到的序列。
一个因子 \(d\mid n\) 等价于选择指数向量 \((a_1,\dots,a_k)\),其中 \(0\le a_i\le e_i\),对应 \(\displaystyle{d=\prod_{i=1}^k p_i^{a_i}}\)。
而 “严格整除” 等价于向量的逐坐标不减且不全相等:\((a_i)\prec(b_i)\) 当且仅当 \(a_i\le b_i\) 对所有 \(i\) 成立且至少一处严格小于。
所以 \(g(n)\) 只由指数集合 \({e_1,\dots,e_k}\)(按降序写成 \(e_1\ge e_2\ge\dots\ge e_k\))决定,与具体用了哪些质数无关。记这个函数为\(g(e_1,\dots,e_k).\)
接下来,我们尝试枚举所有可能的指数签名,若指数签名为 \((e_1,\dots,e_k)\),在所有具有该签名的数中,最小的一个一定是把大指数分配给小质数:\(\displaystyle{n_{\min}=\prod_{i=1}^k P(i)^{e_i}.}\)其中\(P(i)\)表示第\(i\)个质数。
若 \(n_{\min}>N\),则任何具有该签名的 \(n\) 都会超界,因此该签名可以直接丢弃。
于是我们可以递归生成所有满足 \(n_{\min}\le N\) 的非增序列 \((e_1\ge e_2\ge\dots)\)。
但是,直接用\(\displaystyle{g(n)=\sum_{d\mid n,d<n} g(d)}\)会遇到因子数太多的问题。这里引入\(\displaystyle{G(n)=\sum_{d\mid n} g(d).}\)
对 \(n>1\),由于 \(g(n)\) 恰好等于所有真因子的 \(g\) 之和,所以\(\displaystyle{G(n)=g(n)+\sum_{d\mid n,d<n} g(d)=2g(n),(n>1).}\)并且 \(G(1)=g(1)=1\)。
设 \(n\) 的不同质因子为 \({p_1,\dots,p_k}\)。一个因子 \(d\) 是真因子当且仅当它在至少一个质因子方向上少拿了指数,也就是对某个\(i\),有\(d\mid \dfrac{n}{p_i}.\)
因此真因子集合是并集:\(\displaystyle{\{d\mid n,\ d<n\}=\bigcup_{i=1}^k \{d\mid n/p_i\}.}\)
对并集做容斥,就得到
\[ g(n)=\sum_{\emptyset\ne I\subseteq\{1,\dots,k\}}(-1)^{|I|+1}G\left(\frac{n}{\prod_{i\in I}p_i}\right). \]
对签名 \(\mathbf e=(e_1,\dots,e_k)\),把除以若干个不同质因子理解成对若干个坐标各减 \(1\)”。记 \(\mathbf 1_I\) 为在 \(I\subseteq \{1,\dots,k\}\) 上为 \(1\)、其余为 \(0\) 的向量,对于非空签名,则有\(G(\mathbf e)=2g(\mathbf e)\),因此可以写出递推式:
\[ G(\mathbf e)= \left \{\begin{aligned} &1 & & \text{if}\quad e=\varnothing \\ &2\sum_{\emptyset\ne I\subseteq\{1,\dots,k\}}(-1)^{|I|+1}G(\mathbf e-\mathbf 1_I) & & \text{else} \end{aligned}\right. \]
实现时把 \(\mathbf e-\mathbf 1_I\) 做完后重新排序并去掉 \(0\),即可回到签名的规范形。
最终我们对每个签名 \(\mathbf e\) 计算 \(m=g(\mathbf e)\)。
- 若 \(m>N\):不可能是答案,跳过。
- 若 \(m\le N\):再分解 \(m\),取其指数多重集 \(\mathrm{sig}(m)\)。如果 \(\mathrm{sig}(m)=\mathbf e\),则 \(g(m)=g(\mathbf e)=m\),说明 \(m\) 本身就是一个满足条件的 \(n\)。
把所有这样的 \(m\) 去重求和即可(并注意 \(n=1\) 也满足 \(g(1)=1\))。
代码
1 | import itertools |