树上睡觉 / sleep
树上睡觉 / sleep
给定一个叶子深度全部相同的树,根为 \(1\)。对于每个非叶节点指定一个重儿子 \(c_u\)。给定大小为 \(m\) 的集合 \(b\)。
考虑定义 \(\mathrm{dis}(u, v)\) 表示从 \(S = 0\) 开始,从 \(u\) 到 \(v\) 所需操作的最小次数,有两类操作:
- \(S \gets S + 1\),并从当前节点移动到其重儿子。
- \(S \gets S - 1\),选择一个 \(k \in b\),满足当前节点的 \(k\) 级祖先存在,并从当前节点移动到其 \(k\) 级祖先。
如果不存在 \(u\) 到 \(v\) 的合法操作路径,则 \(\mathrm{dis}(u, v) = 0\)。
求 \(\sum_u\sum_v \mathrm{dis}(u, v)\)。
\(1 \le n \le 2 \times 10^5, 1 \le m \le 100\)。
考虑链的情况,注意到叶子节点是无法移动的,所有从叶子出发的 \(\rm dis\) 均为 \(0\)。可以先为每一个向上跳的操作匹配一个向下走的操作,则不再需要考虑 \(S\),可以等价成向上跳 \(b_i - 1\) 级祖先。当 \(b_i \ge n\) 时,显然不合法。
除了叶子以外,我们发现,如果确定了相对距离,总有一种操作方式使得行走永远处于链内(走到叶子或不能往下走了再往上跳)。令 \(f_i\) 表示向上跳 \(i\) 步所需最小操作次数,向上跳的过程是一个完全背包,在向上跳做到了 \(2(n-1)\) 后再开始往下走即可。
拆贡献算答案是简单的。
再把问题放到树上看,考虑行走过程会先往上跳到 lca 所在链,然后向下走到 \(v\) 结束。简单的观察是,叶子深度相同,所以所有剖分均是长链剖分。
你发现对于子树内的叶子,跳跃高度限制是相同的,他们可以共用同一个 \(f\)。在每个长链头上做 DP,复杂度 \(\mathcal O(\mathrm{len} \times m)\),总 \(\mathcal O(n \times m)\)。接下来考虑长链上的点必然作为 \(v\),算一下各类 \(u\) 的贡献。
- \(u, v\) 都处在长链上,那么计算方法与链情况相同
- \(u\) 处在当前长链下面、侧面挂的轻儿子链上,根据刚刚行走过程的分析,首先要消耗 \(f_{\mathrm{dep}_u - \mathrm{dep}_{\mathrm{fa}[\mathrm{top}[u]]}}\) 的代价。接下来向下走可以批量地处理 \(v\),向下走部分贡献是等差数列求和。暴力遍历深度,对同深度 \(u\) 一并统计,总复杂度 \(\mathcal O(n)\)。

浙公网安备 33010602011771号