给定 $n$ 个点的有根树,每个顶点有权值 $1$ 或 $0$. 请确定一个拓扑序,即父结点先于子结点出现,使得该拓扑序的权值序列逆序数最小。
$n \le 2\times10^5$.
假设两棵子树的方案已知,要合并它们。
前导 $0$ 肯定抽到前头来,后缀 $1$ 肯定排到最后去。考虑两子树的形如 $11\cdots100\cdots0$ 的段孰先孰后,反正混在一起肯定不优。
假设第一棵子树的这样的段是 $A_1\times1+A_0\times0$, 第二棵子树的这样的段是 $B_1\times1+B_0\times0$, 则第一段放在前面产生逆序数 $A_1B_0$, 否则产生逆序数 $A_0B_1$.
所以第一棵子树的这个段在前等价于 $A_1B_0 \le A_0B_1$, 也就是 ${A_1 \over A_0} \le {B_1 \over B_0}$.
我们把形如 $11\cdots100\cdots0$ 的段叫做 $\boldsymbol{01}$ 段,其 $1$, $0$ 的个数比叫做该 $01$ 段的密度。
前导 $0$ 也就是 $0\times1+n\times0$, 同理后缀 $1$ 就是 $n\times1+0\times0$, 可以统一处理。因此密度的取值从 $0$ 到 $+\infty$ 不等。
如果同一棵子树里,出现了两个相邻的 $01$ 段,前者密度大于后者,那么我们根据上述引理推断,不可能有另一个段插在它们中间,因此它们被合并成一个段。
这样的段,由于不可再分,在表现上,和把这其中所有的 $1$ 放在前头、所有的 $0$ 放在后头得到的 $01$ 段并无不同。
因此,我们就得到了算法:
按任意逆拓扑序遍历树,用当前的答案中的每个 $01$ 段。遍历一个点时,先把所有子点的段按有序数组合并起来,然后在前头插入一个由其权值组成的短 $01$ 段,不断合并(顺便记录其中逆序数)直到该段密度严格递增。
最后把所有段取出来,再计算一次逆序数。
这里需要维护有序数组的归并、取出/删除/插入有序数组的最小元,这些都可以用斜堆办到。
标解思路类似,但是用的是堆和并查集来维护合并操作。我的这做法貌似快些,截至提交时为 AtCoder 速度第一名。
原文:https://www.cnblogs.com/nealchen/p/12285544.html