##介绍
解决树相关问题的算法的时间复杂度经常与树的深度有关,如果能使树的深度变成 $O(log_2n)$ 级别的,那么很多树相关的问题就迎刃而解了,而实现这一目标的一个重要方法就是树分治。
树分治的中心思想就是将一个规模很大的问题分解为几个两个相同形式的子问题,然后进行合并。
我们首先面临的问题就是如何将一颗树最优地划分为两块,而本文所涉及的点分治便是划分方法的其中一种;其思想主要是以一个点为基准,然后处理过此点的链或点对,至于其它的不断分治下去处理。
我们称作为分治基准点的这个点为重心,那么我们肯定希望它的子树大小比较平均,也就是说,我们要找到一个最大子树最小的点作为重心,具体做法很简单,注意考虑在深度优先遍历意义下作为点祖先的那些点组成的子树即可。
具体做法如下:
1 | bool vis[MAXN];int rt; |
不难证明:点分治的时间复杂度为 $O(nlog_2n)$.
统计点对距离
这是点分治的直接应用,对于每个分治重心计算经过这一重心的点对的贡献。但是这样会产生重复(不是最短路径经过重心的情况),故之后还应减去重复计算的贡献。
常见的问题是求距离 $<,\leq,=,\geq,>k$ 的点对个数。
那么对每个重心统计答案的时候当然不能 $O(n^2)$ 暴力统计,我们可以卡两个指针,$O(n)$ 扫一遍即可。
[POJ1741] Tree
给出一颗 $n$ 个点的树,求距离不大于 $k$ 的点对个数
$n\leq10^4$
这是楼天城男人八题之一,很出名的点分治题目。现在看来当然很裸……
1 |
|
[BZOJ1316] 树上的询问
给定一棵 $n$ 个节点的树,共 $q$ 次询问,每次询问两点对之间是否存在一个长度为 $L_i$ 的路径。
$n\leq10^4,q\leq100$
询问长度距离某值的点对其实就是小于等于的数量减去小于的数量。
1 |
|
[BZOJ2152]聪聪可可
给出一颗树,用分数表示出树上所有点对的距离和等于是 $3$ 的倍数的概率。
$n\leq2\times10^4$
难点在分数……
1 |
|
统计路径条数
路径条数统计的时候就比较简单了,因为不存在重复的问题。
[BZOJ3697]采药人的路径
给定一个 $n$ 个节点的树,每条边都有一个阴阳属性,现在需要选择一条路径,使得中间有一个特殊节点 $p$ (非起点终点)满足从起点到 $p$ 的路径上阴阳平衡,且从 $p$ 到终点的路径上也阴阳平衡。
$n\leq10^5$
1 |
|
动态点分治
###[BZOJ1095][ZJOI2007] Hide 捉迷藏
有一棵树,初始时每个点都染成黑色,有两种操作:
- 将一个点的颜色取反;
- 查询距离最远的两个黑点的距离。
点数 $N\leq10^5$ ,操作数 $Q\leq5\times10^5$.
我们发现:这道题如果没有更改操作的话,就是一道裸的点分治题目。有了更改操作以后,显然我们每一次更改之后都跑一次点分治是不现实的,那么为了充分利用信息,我们考虑将点分治过程中的重心建成一棵重心树(可以证明:中心树的节点数等于原树,且深度不大于 $\log_2n$),然后对于每一个重心开两个堆 $B,C$ ,以动态维护信息。
操作一个重心时,我们将子树上每一个点父重心的距离压进这个重心的 $C$ 堆中,然后将这一中心 $C$ 堆堆顶元素压进其的 $B$ 堆中,也就是说:每个重心的 $B$ 堆存的是其以各儿子为根的子树中到此重心距离的大小;那么我们只要在全局开一个 $A$ 堆,将所有重心的 $B$ 堆的堆顶压进去后, $A$ 堆的堆顶就是答案了。这样,我们只要在更改一个点的颜色时对它和它在重心树上的所有祖先进行一次更新即可。
好了,现在只剩下一个问题了:传统的堆是不支持删除操作的,我们需要使用一种可删堆。具体做法是:开两个堆, $extH,delH$ ,分别表示存在的节点和被删除的节点,删除节点就把它压入 $delH$ 中,取堆顶的时候判断一下是否被删除了,再弹出即可。
因为这道题荒废了半天
1 | /************************************************************** |