有比较不错的树上差分 $\mathcal O(n)$ 做法,也可以使用点分治 $\mathcal O(n\log n)$ 解决。
点分治做法不再赘述,着重梳理一下线性树上差分。
考虑求出 $t_{x,i}$ 表示从 $x$ 出发不包括颜色 $i$ 的路径条数。考虑当前对颜色 $i$ 求,简单做法是去掉颜色为 $i$ 的 点及其连边,得到若干联通块,对连通块中点的 $t_{x,i}$ 加连通块大小。但我们显然不能直接这样,因为 $t_{x,i}$ 状态是 $\mathcal O(n^2)$,并且每做一次都需要遍历整棵树。
尝试一次遍历求出 $t’_{x}=\sum t_{x,i}$,考虑计算每个点删去之后对该点颜色 $c_x$ 的答案的影响。设 $x$ 子节点为 $y_i$,子树 $y_i$ 变得互相独立,且 $y_i$ 包含的连通块要加上其大小 $s_i$。连通块与子树 $y_i$ 中的点 $z$ 满足 $c_z=c_x$ 有关,因为其也形如对子树的贡献,容易考虑树上差分。对于 $z$,我们对于每种颜色开一个栈维护,每次计算 $x$ 之后遍历的节点 $z$ 并弹出即可。 对于 $s_i$,我们直接维护当前已经计算过的连通块大小 $\operatorname{colsize}(c_x)$,并且在递归子树 $y_i$ 前记录,递归前减去递归即可得到子树 $y_i$ 中连通块大小,即 $s_i=\operatorname{size}(y)-(\operatorname{colsize}(c_x)-\operatorname{colsize}’(c_x))$。显然每个节点只会进出栈一次。最终 $t’_{x}$ 为当前节点 $x$ 到根节点的差分数组之和。时间复杂度为 $\mathcal O(n)$。
代码理解后很好写,注意最后单独处理根节点。
Code
1 |
|