QOJ.ac

QOJ

Type: Editorial

Status: Open

Posted by: Anonymous

Posted at: 2026-09-14 11:32:41

Last updated: 2026-09-14 11:33:56

Back to Problem

C = 27 Solution by ChatGPT

可以。我得到了一个确定性、最坏情况下 (C\le 27) 的构造,并写出了完整实现。

它是在公开的 (C\le28) 方案所用的“链分解、已知骨架标记、单点叶子子集计数”框架上,合并三处改进得到的。(QOJ)

这不是随机测试得到的经验上界:下面的成本公式已经对全部 (1544) 种必要参数情况精确枚举,特殊编码矩阵也已穷举验证。不过,我没有提交官方评测,也没有证明 (27) 最优。

完整 C++ 实现 · 独立上界验证脚本 · 完整代码包,含本地模拟器与说明

下面重点讲清楚:为什么可以从 (28) 降到 (27)


一、沿用的基础框架

依次将叶子 (i) 灌到 insert 返回 false,记成功次数为 (\ell_i)。这次新增的结点形成一条以叶子 (i) 结尾的链,各链不相交,覆盖整棵树,因此

$$ N=\sum_i\ell_i. $$

称 (\ell_i\ge2) 的链为长链,(\ell_i=1) 的链为单点叶子。所有内部结点都在长链中。

首轮对前 (8) 条链分别使用颜色 (8,7,\ldots,1),其余使用 (0)。一次 collect 后,既知道所有链长,也能恢复前 (8) 条链。令

$$ D=\text{全部长链数},\qquad a=\text{前 \(8\) 条链中的长链数}, $$

那么尚未定位的单点叶子数是

$$ s=M-D-8+a. $$

当 (M\le8) 时,直接给所有链使用递减的不同颜色,一轮就能恢复,(C=M\le8)。(QOJ)

后面还用到原方案的一个骨架标记引理:

已知 (d) 条长链时,适当省略已知叶子,可以用

$$ b=\lceil\sqrt d\rceil $$

种颜色标记已知骨架,使同一父结点的内部子结点,可以通过“自身颜色、其第一个骨架子结点的颜色”唯一识别。这不依赖同值兄弟的遍历顺序。(QOJ)

以下是新的部分。

二、改进一:长链颜色对从“矩形”扩成“三角形”

固定全局最大球值 (B)。当前已经恢复 (d) 条长链,令

$$ b=\lceil\sqrt d\rceil,\qquad h=B+1-b. $$

将已知骨架的颜色整体平移到

$$ h,h+1,\ldots,B. $$

我们还允许预留 (f) 种最低颜色

$$ 0,1,\ldots,f-1 $$

用于单点叶子的观测;普通补链轮次取 (f=0)。

新的链编码

一条新长链使用颜色序列

$$ \boxed{x,\ y,\ B,\ B,\ldots} $$

其中

$$ f\le x

关键安排:按链首颜色 (x) 非增的顺序枚举颜色对,再依照原叶子顺序分配给新长链。

因此,一轮可使用的不同颜色对数量是

$$ \begin{aligned} A(B,b,f) &=\sum_{x=f}^{B-b}(B-x)\\ &=\boxed{T(B-f)-T(b-1)}, \end{aligned} $$

其中

$$ T(z)=\frac{z(z+1)}2. $$

没有提前观测时,容量就是

$$ \boxed{A(B,b,0)=T(B)-T(b-1)}. $$

例如 (B=17,b=3),容量为

$$ 153-3=150. $$

为什么这样还能解析?

考虑链首颜色为 (x) 的一条链。

任何挂在它内部结点上的其他新链,都比它更晚加入。所以根据颜色对的分配顺序,那些侧挂链的链首颜色都满足

$$ x'\le x. $$

与此同时,当前链的后续结点颜色严格大于 (x):

$$ y>x,\qquad B>x. $$

于是,读到链首 (x) 后,可以:

先递归解析所有链首颜色不超过 (x) 的侧挂链,以及颜色小于 (f) 的单点叶子;接下来遇到的第一个大于 (x) 的值,就是这条链第二个结点的 (y)。

颜色对 ((x,y)) 唯一确定链身份,也就确定了链长。随后按已知长度继续解析即可。

这里有两个很容易写错的地方:

  • 链首必须按非增顺序分配。否则侧挂链的链首可能超过当前链首,无法区分侧挂分支和链的延续。
  • 解析新链时使用局部边界 (x),不能一直使用全局边界 (h)。第二个颜色 (y) 完全可以小于 (h),它仍然是链的延续。

这就允许使用整个三角形区域

$$ x

而不是要求第二个颜色一定属于已知骨架的高位颜色区间。

三、改进二:最后一次补长链,同时做第一次单点计数

单点叶子不一定要等到下一轮才开始处理。

最后一次补长链的轮次中,当全部长链的球都放好以后,所有内部结点已经被占。此时再向一个尚未定位的单点叶子插球,球一定留在该叶子上。

因此,这一轮既可以恢复最后的长链,也可以为单点叶子做一次计数观测。

预留的 (f) 种低位颜色,正是用于这件事。因为它们比所有新链链首颜色都小,解析时可以直接把它们识别为单点叶子,并统计它们挂在哪个内部结点下面。

合并后能省多少?

全部长链恢复以后,骨架需要

$$ t=\lceil\sqrt D\rceil $$

种颜色,所以每个后续纯计数轮次有

$$ q=B+1-t $$

种颜色可用于单点叶子。

定义 (F(r)) 为:

使用同一种颜色进行 (r) 次子集计数,可以唯一识别一个组中至多 (F(r)) 个叶子的归属。

假设最后补长链时预留了 (f) 种颜色,后面再做 (r) 轮纯计数。

那么 (\min(q,f)) 个组能得到 (r+1) 次观测,其余后续组得到 (r) 次观测。若 (f>q),额外的提前颜色还可以各自直接定位一个叶子。

总容量为

$$ \boxed{ G(q,f,r) = qF(r) +\min(q,f)\bigl(F(r+1)-F(r)\bigr) +\max(0,f-q) }. $$

特别地,

$$ G(q,f,0)=f. $$

这次合并不是要求所有组都提前观测。只让一部分组多获得一行编码,就足以改善最坏边界。

四、改进三:用九次计数识别十六个变量

这里的计数是整数计数,不是模 (2) 运算

设某一组有 (g) 个未知单点叶子。对每个内部结点 (u),用

$$ z\in\{0,1\}^g $$

表示组内哪些叶子的父结点是 (u)。

选择一个二进制矩阵

$$ A\in\{0,1\}^{r\times g}. $$

第 (k) 轮插入第 (k) 行中取值为 (1) 的那些叶子,就会在 (u) 处观察到

$$ Az. $$

因此需要

$$ Az=Az'\Longrightarrow z=z', \qquad z,z'\in\{0,1\}^g. $$

原方案的一般构造提供

$$ W(r)=\sum_{x=1}^{r}\operatorname{popcount}(x) $$

列,其中 (W(8)=13,\ W(9)=15)。(QOJ)

我找到并验证了一个 (9\times16) 的矩阵。因此这里改为

$$ \boxed{ F(r)= \begin{cases} 16,&r=9,\\ W(r),&r\ne9. \end{cases}} $$

矩阵的完整常量

把每列视为一个九位二进制整数,最低位对应第 (0) 行,十六列依次为:

466, 231, 382, 476, 441, 391, 459, 210,
364, 62, 470, 341, 59, 60, 91, 306

它的全部

$$ 2^{16}=65536 $$

个整数向量子集和两两不同。

下面这段代码就能独立验证,不使用求解器,也不依赖随机测试:

columns = [
    466, 231, 382, 476, 441, 391, 459, 210,
    364, 62, 470, 341, 59, 60, 91, 306,
]

sums = {(0,) * 9}

for mask in columns:
    column = tuple((mask >> row) & 1 for row in range(9))
    added = {
        tuple(x + y for x, y in zip(old, column))
        for old in sums
    }
    assert sums.isdisjoint(added)
    sums.update(added)

assert len(sums) == 65536

矩阵是离线搜索得到的固定常量,最终算法不进行随机搜索

实现中,九个计数分别用五个二进制位保存,然后穷举至多 (65536) 个子集精确查表。保存的是完整键值,不依赖随机哈希不碰撞的假设。其他行数仍用一般构造的直接解码。

五、如何严格得到最坏 (C\le27)

前面的改进给出了每轮的精确能力,剩下只是一个很小的整数动态规划。

1. 普通补长链轮次

枚举

$$ 8\le B\le26. $$

$$ dp_B[d] $$

表示从首轮已知的 (a) 条长链出发,额外恢复到 (d) 条长链最少需要多少次 collect

初值

$$ dp_B[a]=0. $$

在状态 (d),令

$$ b=\lceil\sqrt d\rceil. $$

当 (b\le B) 时,一轮能增加至多

$$ T(B)-T(b-1) $$

条长链,所以对所有

$$ d

转移

$$ dp_B[e]\gets \min\bigl(dp_B[e],dp_B[d]+1\bigr). $$

这里枚举所有批次终点,而不是假设每轮尽可能多加一定最优。

2. 最后一轮预留多少计数颜色?

假设最后一轮开始前,已知 (d<D) 条长链。

$$ b=\lceil\sqrt d\rceil, $$

并找最小整数 (n),使

$$ T(n)-T(b-1)\ge D-d. $$

由于本轮预留 (f) 种颜色后的容量是

$$ T(B-f)-T(b-1), $$

所以最大的可行预留量就是

$$ \boxed{f=B-n}. $$

要求 (f\ge0)。

之后,找一个 (r),满足

$$ G\left(B+1-\lceil\sqrt D\rceil,\ f,\ r\right)\ge s. $$

总成本为

$$ \boxed{ C\le B+2+dp_B[d]+r }. $$

其中两次固定的 collect 分别是首轮和最后的“补长链兼计数”轮次。

两个退化情况单独处理:

当 (s=0) 时,

$$ C\le B+1+dp_B[D]. $$

当 (D=a) 时,不需要再补长链,只需选择

$$ qF(r)\ge s, $$

得到

$$ C\le B+1+r. $$

3. 为什么只需检查 (1544) 种参数?

对 (M=200),枚举

$$ 1\le D\le200, $$

$$ 1\le a\le\min(8,D), $$

$$ s=200-D-8+a\ge0. $$

一共有 (1544) 组合法参数。

对每一组,按上述公式枚举 (B,d,r),全部都能找到

$$ C\le27 $$

的计划。

固定 (D,a) 后,较小的 (M) 只会减少 (s),不会增加所需轮数,因此也覆盖所有 (M>8) 的情况。

独立验证脚本的实际输出是:

matrix: 65536 / 65536 distinct integer subset sums
parameter cases: 1544
worst minimum C in this construction: 27
one worst parameter triple (D, a, s): (37, 1, 156)
ALL EXACT CHECKS PASSED

这里穷举的是成本公式的全部参数,不是若干棵测试树。具体树形与链长不参与这个成本上界,所以这个检查给出的是最坏保证。

六、一个能看出三项改进如何配合的例子

$$ M=200,\qquad D=65,\qquad a=8. $$

那么

$$ s=200-65-8+8=135. $$

选择

$$ B=17. $$

首轮后已知 (8) 条长链,所以

$$ b=\lceil\sqrt8\rceil=3. $$

在补长链的轮次中预留

$$ f=6 $$

种提前计数颜色。此时可以恢复的长链数量为

$$ T(17-6)-T(3-1) =T(11)-T(2) =66-3 =63. $$

而只剩

$$ 65-8=57 $$

条长链,因此一轮就能全部恢复,同时给六个单点叶子组做第一行观测。

之后骨架需要

$$ \lceil\sqrt{65}\rceil=9 $$

种颜色,剩下

$$ q=18-9=9 $$

种计数颜色。

再做八轮,就有:

$$ 6\text{ 个组}\times F(9) + 3\text{ 个组}\times F(8) = 6\times16+3\times13 = 135. $$

恰好定位全部单点叶子。

最终

$$ K=1+1+8=10, $$

所以

$$ \boxed{C=17+10=27}. $$

七、实现与验证情况

完整代码已经实现骨架染色、三角颜色对的递归解析、提前计数、矩阵解码及参数计划选择。恢复过程中只需确定各条链链首的父结点;链内父边由链长直接确定,内部编号可以按题意重新分配。

本地整流程验证通过了 (3689) 个用例,包括全部 (1544) 种参数组合的多种结构、链、星形、随机结构,以及打乱同值兄弟顺序的测试,实际最大 (C=27)。另有 (100) 个随机用例通过了 AddressSanitizer 和 UndefinedBehaviorSanitizer 检查。

其他资源也有直接上界。对于 (M>8),由于

$$ B\ge8,\qquad B+K\le27, $$

可得

$$ K\le19. $$

首轮至多调用 (N+M) 次 insert,后面每轮至多 (N) 次,因此

$$ \boxed{ \#\mathrm{insert}\le KN+M \le19\cdot1000+200 =19200 }, $$

远低于题面的 (500000) 次限制。每个放球阶段结束都会 collect,返回答案时机器为空。

代码包里的复现命令为:

python3 verify_bound.py

g++ -std=c++17 -O2 -DNDEBUG \
    ballmachine_c27.cpp self_test.cpp -o self_test

./self_test 200

因此,目前这里得到的确切改进是:

$$ \boxed{\text{题述全部约束下,可以将确定性最坏上界从 }28\text{ 降至 }27.} $$

这是一个经过完整实现和有限证书验证的可达上界;并不意味着 (26) 不可能,也不意味着已经证明 (27) 最优。

Comments

avatar
sjw712
https://qoj.ac/submission/2954205