QOJ17186 哈夫曼树 II
模拟赛 T3。我对此题的开发不足百分之五十。
题意
给定长为 \(n\) 的序列 \(a\),你需要构造一个满二叉树,使得有恰好 \(n\) 个叶子,将 \(n\) 个 \(a_i\) 填到叶子上,令 \(a_i\) 所填的叶子深度为 \(d_i\)(根的深度为 0),则权值是 \(\sum a_i\cdot d_i\),求权值最小值。
\(n\le 2^{18},a_i\le 10^9\)
分析
先考虑一个深度序列是否合法,最优解肯定是把较大的 \(a_i\) 填到较小的深度里。如果你会手玩,或者你见过这个也许很经典的结论,你会发现 \(\sum 2^{-d_i}=1\)。考虑先让 \(d_i=0\),然后再依次减少 \(d_i\),这样的话,相当于有一个 \(d\times n\) 的矩阵,每一列要选择一段前缀,选择点 \((i,j)\) 会让总和减少 \(2^{-j}\),增加代价为 \(a_i\)。由于你不选前缀一定不优(因为同样的代价前面减的更多)所以可以把前缀的限制去掉。从下往上考虑每一行,对于第 \(d\) 行,因为要是整数,所以必然选偶数个,根据贪心必然是第一和第二捆绑,第三和第四捆绑,以此类推,捆绑后的权值变为 \(2^{-(d-1)}\),把它丢到第 \(d-1\) 行考虑即可,然后在 \(d-1\) 行再做一遍,直到第 \(1\) 行,此时选出前 \(2(n-1)\) 个点求和即可。
为啥是对的?(以下语言大概率在乱写)首先将贡献拆到每一个点上,一个点的贡献为它子树内的叶子权值和,则我们取出的 \(2(n-1)\) 个点就是树上 \(2(n-1)\) 个点的权值。首先,你手玩一下可以发现一个点永远不会跟它子树内的点连边,其次,第 \(i\) 层的叶子权值往上合并出的点的权值就是第 \(i\) 层点的贡献。所以就大致感性理解了这个题的正确性(?)
如果往上合并用归并将原层的点和往上合的点排序,则暴力做的复杂度是 \(O(nd^2)\) 的。而根据哈夫曼树的经典结论,深度增长大致是斐波那契数列级别即 \(O(\log V)\) 的(大概是 60 多),所以复杂度是 \(O(n\log^2V)\);由于每一层的权值是相同的,所以你从 \(d\) 向 \(1\) 层合并实际上等价于 \(d-1\) 向 \(0\) 合并,所以你往上迭代 \(d\) 次求的就是 \(d\) 的答案,复杂度 \(O(n\log V)\)。

浙公网安备 33010602011771号