运用广泛的合并信息方式。
启发式算法是什么呢?
启发式算法是基于人类的经验和直观感觉,对一些算法的优化。
启发式合并又名 dsu on tree,常常运用在树上信息的合并,常见的并查集的按秩合并就是其中一种。并且常常与树链剖分相结合,从而达到较优的时间复杂度。
1.图论
1.1 重链剖分
1.1.1 CF275D Tree and Queries
考虑离线。我们对于一颗子树,先计算它的所有轻儿子的答案,但是删除轻儿子的信息。然后计算重儿子的答案并直接继承重儿子的信息。再暴力将所有轻儿子的信息合并上,最终计算该节点的答案。这样做是 $\mathcal O(n\log n)$。
看起来很不对?我们来分析一波。对于每一个点,到根节点,每有一条轻边就会使该点被暴力计算一次。根据树链剖分经典结论,每个点到根节点的轻边数不会超过 $\mathcal O( \log n)$ 条。那么每个点最多只会被暴力计算 $\mathcal O( \log n)$次,那么时间复杂度就是 $\mathcal O(n\log n)$ 。
当然也可以线段树合并。
Code
1 |
|
通过上面的例子可以发现,启发式合并需要一个点的贡献很容易加上或者删除。
1.2 长链剖分
常常用来优化状态包含深度的 DP。
1.2.1 P3565 [POI2014]HOT-Hotels
设计 DP 状态,$f_{x,k}$ 表示子树 $x$ 中与 $x$ 距离为 $k$ 的点的个数。$g_{x,k}$ 表示 $dis(u,lca)=dis(v,lca)=dis(lca,x)+k$ 的 $(u,v)$ 个数。设 $y,y_1,y_2$ 均为 $x$ 的儿子节点,那么状态转移方程为:
通过前缀和容易做到 $\mathcal O(n^2)$。注意到状态与深度有关,于是我们可以用长链剖分优化 DP。对于当前点 $x$,我们直接继承它的长儿子的状态,再其余儿子合并计算。容易发现这样做显然只会计算长链上的每个节点一次,而长链总长度是 $\mathcal O(n)$ 的。所有总的复杂度就是 $\mathcal O(n)$。具体地可以通过指针实现对长儿子信息地直接继承。
Code
1 |
|