1 条题解
-
0
1. 问题回顾
我们有一棵大小为 的完美二叉树。
初始 。
执行 次 add 操作:每次选一个节点 ,将根到 路径上所有节点的值 。
等价于:最终 ,其中 是节点 被选为 add 目标的次数,
满足 , 整数。题目要求:最终形成的堆是一个 deterministic max-heap,即:
- 满足 max-heap 条件(自动成立,前面已证);
- 第一次 pop 操作 deterministic(每次比较子节点值不相等);
- 第二次 pop 操作 deterministic(在第一次 pop 改变树之后,再次比较子节点值不相等)。
我们要数不同的最终 数组个数(即不同的 分布),模素数 。
2. 关键转化(标准解法思路)
已知结论(见原题解):
第一次 pop 的路径 和第二次 pop 的路径 必须是 从根到叶子的两条路径(可能相同),且满足:- 对 上每个内部节点 ,第一次 pop 前 ;
- 对 上每个内部节点 ,第一次 pop 后 。
记 。
第一次 pop 的过程等价于:从根开始,每次选择子节点中值较大的方向(由 的符号决定,若 则去左孩子, 则去右孩子),一直走到叶子。
所以 由每个节点上的 符号唯一确定(不允许 )。第一次 pop 后, 更新:
设 ,其中 , 是叶子。
新值 (),,其余节点 。于是第二次 pop 时,我们需要新的 对 上的 都不为 0。
3. 用 表示 和
原树中:
$b_u = \sum_{v \in \text{subtree}(2u)} c_v - \sum_{v \in \text{subtree}(2u+1)} c_v$。第一次 pop 后,对不在 上的节点 , 不变。
对在 上的节点 ():- 它的左孩子可能是 或右孩子是 ,取决于 的正负;
- 第一次 pop 会把 变成 ,这会影响 (即 ),因为 的表达式中有 的项(来自 或 )。
经过仔细推导(可参考 CF 原题解), 可以写成 的线性组合,且当 在 上时,要求 ,这变成对 在 上的线性不等式的约束。
4. 计数等价问题
事实上,最终化简为:
- 选两条根到叶子的路径 (可能相同);
- 分配 ,,使得:
- 对 上每个非叶节点 ,;
- 对 上每个非叶节点 ,;
- 不同 分布给出不同 (因为 是单射)。
并且已知结论:对固定的 ,满足条件的 分配方案数只依赖于 和 的 形状(即两条路径的交、差结构),以及 和 。
5. 树的结构与对称性
由于二叉树是完全且对称的,所有根到叶子的路径在组合计数上等价,除了 与 的相对位置。
可以证明: 和 的关系仅由它们的 最近公共祖先 (LCA) 的深度 决定( 表示根, 表示叶子)。
此外, 和 在 LCA 以下的走向(左或右)不影响计数,只影响 的符号,而符号由 分配决定,但总方案数相同。所以我们可以按 LCA 的深度 分类。
6. DP 状态设计(对应代码)
代码中的 DP 定义:
dp[j]:处理到当前深度时,对于一条路径 (当前已经处理了根到当前深度的节点),该路径上的 之和为 的方案数(这里的“方案”是指在这个深度以下的子树分配 ,使得已处理的 部分满足 条件)。dp2[l][r]: 的 和为 , 的 和为 的方案数(这里 是两条路径各自已分配的 和,不包括 LCA 以上的部分)。
每次向下扩展一层时:
- 新节点 有两个孩子, 会走向其中一个孩子(由 的正负决定), 也会走向某个孩子(可能不同)。
- 两个孩子子树是独立的, 分配独立。
- 组合数来自:将 个 add 操作分配到 个节点( 是当前深度, 个可能的新起点)的方法数。
7. 组合数推导(对应
binom和binom2)当我们在深度 (根深度 0)时,考虑将 次 add 操作分配到当前子树的某些节点上,这些节点在子树中的位置由路径 和 决定。
实际上,标程中的
binom[x]计算的是:把 个不可区分的 add 操作分配到 个节点(这些节点是当前深度下,某个子树的根集合),允许重复(每个节点可以分配任意次)。
分配方案数 = 。因为 ,所以
binom[x] = C(x + 2^i - 1, x)对 取模。
代码中binom[0]=1,递推:binom[j] = binom[j-1] * (pow2[i] + j - 2 + p) % p这是因为 $\binom{x+m-1}{x} = \binom{x+m-2}{x-1} \cdot \frac{x+m-1}{x}$,分母 用阶乘逆元处理,分子是 的乘积。
最后乘以faci[x]即 。
binom2类似,但 (因为可能涉及两个深度的组合)。
8. 转移方程解释
第一部分(处理 延伸)
for (int j = 0; j <= k; j++) for (int x = 0; x < j && j + x <= k; x++) nxt[j + x] = (nxt[j + x] + dp[j] * binom[x]) % p;dp[j]:当前路径 已有的 和 = 。- 我们选择 下一步走向左孩子(由 决定),并在这个孩子的子树中分配 个 add 操作(这些操作影响 不为 0,因为左子树和大于右子树和的条件可以通过 分配实现)。
- 实际上, 表示在当前节点 的右子树中分配的操作数,但这是代码中的简化,具体推导需结合 条件化为:左子树和 - 右子树和 ,通过给右子树分配 个操作( 从 0 到 ),可以保证 。
- 分配 到右子树的 个节点(该深度的右子树根集合)的方式数 =
binom[x]。
然后
nxt再做前缀和(for (int j=1; j<=k; j++) nxt[j] = (nxt[j-1] + nxt[j]) % p),这是为了与 部分合并做准备。
第二部分(合并 和 的转移)
nxt2的更新分两块:第一块: 和 在当前节点 分叉(即 走向与 不同的孩子)。
tmp[l][r]记录 的 和 = , 的 和 = 的方案数(这里 不包含当前节点)。
枚举 (来自dp[l])和 (来自binom[r]分配给与 不同的分支)。
最后用nxt[r]乘上去(nxt[r]已经包含了 在前缀中的可能性)。第二块: 和 在当前节点 走同一个孩子(即 到目前为止)。
这时dp2[l][r]表示两条路径已有的 和分别为 和 ( 因为 和 重合部分一致,但 可能大于 因为 可能短一些?不,这里是两条路径各自的和,不是差)。
然后枚举它们共同走向一个孩子,分配 到该孩子的子树(binom2[r])。
9. 最终答案
最后
dp2[l][r]表示:处理完所有深度后, 的 和 = , 的 和 = 的方案数,且 。
总方案数 = 。再乘上 :为什么?因为最后一步 和 走到叶子时,最后一步的选择(叶子是左还是右)不会影响 条件(叶子无子节点),但不同的叶子选择产生不同的 分布,并且所有对称的叶子分配数 = 叶子数 = 。所以总 deterministic max-heap 数 = ()。
10. 复杂度
理论上,但代码通过前缀和优化到 ,对于 可行。
11. 总结
该标程实现了一个 DP,在树上按深度递推,枚举两条路径 的 和分配,利用组合数 分配操作到 个节点,最终统计满足 deterministic pop 条件的 分布数,并乘以叶子选择数 得到答案。
- 1
信息
- ID
- 6542
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 4
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者