$\newcommand{le}{\leqslant}\newcommand{ge}{\geqslant}$

[AGC023F]01 on Tree 另解

题目链接

题意

给定 $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 速度第一名。

posted @ 2020-02-08 22:50  nealchen  阅读(514)  评论(0编辑  收藏  举报