Project Euler 334
Project Euler 334
题目
Spilling the beans
In Plato’s heaven, there exist an infinite number of bowls in a straight line.
Each bowl either contains some or none of a finite number of beans.
A child plays a game, which allows only one kind of move: removing two beans from any bowl, and putting one in each of the two adjacent bowls.
The game ends when each bowl contains either one or no beans.
For example, consider two adjacent bowls containing \(2\) and \(3\) beans respectively, all other bowls being empty. The following eight moves will finish the game:

You are given the following sequences:
\[\begin{aligned} &t_0=123456,\\ &t_i=\left\{\begin{aligned} &\dfrac{t_{i-1}}{2}, & &\text{if }t_{i-1}\text{ is even} \\ &\left\lfloor\frac{t_{i-1}}{2}\right\rfloor\oplus 926252, & &\text{if }t_{i-1}\text{ is odd} \end{aligned}\right. \\ &\qquad\text{where }\lfloor x\rfloor\text{ is the floor function }\\ &\qquad\text{and }\oplus\text{is the bitwise XOR operator.}\\ &b_i=(t_i\bmod2^{11}) + 1 \end{aligned}\]
The first two terms of the last sequence are \(b_1 = 289\) and \(b_2 = 145\).
If we start with \(b_1\) and \(b_2\) beans in two adjacent bowls, \(3419100\) moves would be required to finish the game.
Consider now \(1500\) adjacent bowls containing \(b_1, b_2,\dots, b_{1500}\) beans respectively, all other bowls being empty. Find how many moves it takes before the game ends.
解决方案
这题最早出自Google Code Jam 2010 R3 的 C题。
把每个碗看成整数直线上的一个格点。第 \(i\) 个碗放在位置 \(i\)(从 \(0\) 开始),碗里豆子数就是该点上的数 \(c_x\)。
一次合法操作(题目唯一允许的 move)是:在某个位置 \(x\) 上取走 \(2\) 颗豆子,并分别放到相邻的 \(x-1\) 与 \(x+1\) 上。也就是\((c_{x-1},c_x,c_{x+1})\mapsto(c_{x-1}+1,c_x-2,c_{x+1}+1).\)当所有位置都满足 \(c_x\in\{0,1\}\) 时游戏结束(达到稳定态)。
设所有豆子的位置(带重数)为集合 \({p_1,p_2,\dots,p_n}\),其中 \(\displaystyle{n=\sum_x c_x}\) 为总豆子数。
一次操作把两个豆子从 \(x,x\) 变成 \(x-1,x+1\),而\((x-1)+(x+1)=x+x.\)因此\(\displaystyle{\sum_{k=1}^{n} p_k }\)在所有操作中保持不变。记这个不变量为
\[ S=\sum_{k=1}^{n} p_k=\sum_x c_xx. \]
同理,\((x-1)^2+(x+1)^2 = x^2+x^2+2.\)因此
\[ Q=\sum_{k=1}^{n} p_k^2=\sum_x c_x,x^2 \]
在每一步操作中都恰好增加 \(2\)。于是若总步数为 \(M\),则必有\(Q_{\text{final}}=Q_{\text{init}}+2M\Longrightarrow M=\dfrac{Q_{\text{final}}-Q_{\text{init}}}{2}.\)
所以问题被转化为:构造最终稳定态并算出其 \(Q_{\text{final}}\);步数立刻由上式给出。
接下来考虑一堆豌豆的稳定态。
先看只有一个位置 \(x\) 上有 \(k\) 颗豆子,其它全空。
由于规则对左右对称,最终稳定态必是“尽量对称地摊开”:
- 若 \(k\) 为奇数:最终是一个连续段,长度为 \(k\),以 \(x\) 为中心:\(\left\{x-\dfrac{k-1}{2},\dots,x+\dfrac{k-1}{2}\right\}.\)
- 若 \(k\) 为偶数:最终是长度为 \(k+1\) 的连续区间里恰好空一个中心点:\(\left\{x-\dfrac{k}{2},\dots,x+\dfrac{k}{2}\right\}\setminus\{x\}.\)
这启发我们用带一个洞的连续段来表示一般的稳定块。
定义一个 H-segment 由三元组 \((x,y,z)\) 表示,其中满足\(x<y\le z,\)并表示在区间 \([x,z]\) 上除了 \(y\) 这一点为空外,其余点都各放一颗豆子,即占据集合 \(\{x,x+1,\dots,z\}\setminus\{y\}.\)
这类块有两个好处:
- 它恰好覆盖了上面“偶数堆有一个洞”的形态;
- “没有洞的连续段”也能纳入:把洞放在最右端 \(y=z\),那么占据集合就是 \([x,z-1]\) 这一整段(右端点空出来)。
对 \((x,y,z)\) 这块:
- 豆子总数为\(K=(z-x+1)-1=z-x.\)
- 位置和为\(\displaystyle{S=\sum_{p=x}^{z}p-y.}\)
- 平方和为\(\displaystyle{Q=\sum_{p=x}^{z}p^2-y^2.}\)
将两个稳定块叠加(把豆子数相加)后,如果它们的占据范围相交或相邻,就会在交界处产生位置上豆子数 \(\ge2\),需要进一步进行操作消解冲突。
在一维直线上,这种冲突沿着区间传播,但最终连通块仍只会留下至多一个洞:直观上,你可以把“多出来的一颗豆子”看成在连续段中推动洞的位置,洞不会凭空变成两个。于是这整个连通块稳定后仍是一个 H-segment。
因此当两个块需要合并时,只要知道合并后的:
- 豆子总数 \(K\)(显然相加);
- 位置和 \(S\)(由观察 1,不变,因此也相加);
就能唯一确定新的 \((x,y,z)\)。
对任意给定的 \(x\) 与 \(K\)(注意 \(z=x+K\)),满区间 \([x,x+K]\) 的和是\(\displaystyle{\sum_{p=x}^{x+K}p=\frac{(K+1)(2x+K)}{2}.}\)而真实占据要减去洞 \(y\),其中洞满足 \(y\in{x+1,x+2,\dots,x+K}\),所以 \(S\) 的可能范围为:
- 当洞取最大 \(y=x+K\) 时,\(S\) 最小:\(\displaystyle{S_{\min}=\frac{(K+1)(2x+K)}{2}-(x+K)=\frac{K(2x+K-1)}{2}.}\)
- 当洞取最小 \(y=x+1\) 时,\(S\) 最大:\(\displaystyle{S_{\max}=\frac{(K+1)(2x+K)}{2}-(x+1)=\frac{K(2x+K+1)}{2}-1.}\)
因此 \(x\) 必须且只需满足\(\dfrac{K(2x+K-1)}{2}\le S<\dfrac{K(2x+K+1)}{2}.\)
这个不等式对整数 \(x\) 有唯一解(区间长度为 \(K\),随着 \(x\) 增大整体平移),所以可以用整数除法先算一个候选,再做 \(1\) 次左右微调就能落到正确的 \(x\)。
一旦 \(x\) 确定,令\(\displaystyle{F=\sum_{p=x}^{x+K}p=\frac{(K+1)(2x+K)}{2},}\)则洞的位置就是\(y=F-S.\)再令\(z=x+K,\)便得到新的 H-segment \((x,y,z)\)。
因此,题目里只有 \(C=1500\) 个非空碗(其余全空)。与其一颗一颗加豆子,不如“一堆一堆”处理:
- 第 \(i\) 个碗在位置 \(p=i-1\),有 \(b_i\) 颗豆子。
- 先把这堆单独稳定成一个 H-segment。
- 用一个栈从左到右存放当前已经处理完的、互不接触的 H-segment。
- 新段如果与栈顶段“接触或重叠”(右端占据点 \(+1\ge\) 新段左端占据点),就弹出栈顶并合并成一个更大的 H-segment,继续与新的栈顶检查,直到完全分离为止;然后把新段压栈。
最终所有段加起来就构成最终稳定态。于是:
- 初始平方和\(\displaystyle{Q_{\text{init}}=\sum_{i=1}^{C} b_i(i-1)^2.}\)
- 终态平方和 \(Q_{\text{final}}\) 为所有 H-segment 的 \(Q\) 之和。
- 答案步数\(M=\dfrac{Q_{\text{final}}-Q_{\text{init}}}{2}.\)
代码
1 | from dataclasses import dataclass |