📅  最后修改于: 2023-12-03 15:40:27.296000             🧑  作者: Mango
树的质心分解是一种基于树的结构的分治算法,其主要目的是将树划分为若干个大小相等或者相差不超过一的子树,其中每一个子树的根节点都是该子树的质心。对于一棵树,质心分解可以被用来解决众多树上面的问题,例如最近公共祖先问题、树形dp问题等等。
以下是质心分解的基本算法步骤:
1.以任意一个节点u(可以是根节点)为根节点,计算整棵树的大小siz[u]并将其记录下来;
2.以节点u为根节点,计算整棵树减去u的子树大小的最大值mx[u]并将其记录下来;
3.对于节点u的每一个子节点v,如果mx[v]<=siz[u]/2,则将节点v加入当前处理的子树中并递归调用该函数;
4.否则,重复2-3步骤直到找到一个子节点满足条件。
5.节点u就是当前子树的质心,处理完该子树后,将其从树上删除。
6.对于根节点,重复2-5步骤直到整棵树的节点全部处理完毕。
下面是具体的算法实现(使用C++语言):
const int N = 1e5 + 5;
vector<int> G[N];
int siz[N],mx[N],ans[N],n;
//u为当前节点
void dfs1(int u,int fa)
{
siz[u] = 1,mx[u] = 0;
for(int i = 0;i < G[u].size();i ++)
{
int v = G[u][i];
if(v == fa) continue;
dfs1(v,u);
siz[u] += siz[v];
mx[u] = max(mx[u],siz[v]);
}
mx[u] = max(mx[u],n - siz[u]);
}
void dfs2(int u,int fa)
{
int p = 0;
for(int i = 0;i < G[u].size();i ++)
{
int v = G[u][i];
if(v == fa) continue;
if(mx[v] <= n / 2) dfs2(v,u);
else p = v;
}
if(p)
{
dfs1(p,0);
dfs2(p,u);
} else ans[u] = u;
}
接下来我们分析一下质心分解算法的时间复杂度,设 $T(n)$ 表示处理一颗大小为 $n$ 的树需要的时间。显然,算法中最主要的部分是 dfs1 函数,它需要遍历整棵树,所以它的时间复杂度是 $O(n)$。而 dfs2 函数最多会被调用 $O(logn)$ 次,因为每一次处理的子树的大小都会至少减少一半。所以,质心分解算法的时间复杂度是 $O(nlogn)$。
质心分解是一种十分实用的树的分治算法,被广泛应用于众多树上的问题。需要注意的是,在实现时要使用递归方式,同时为了避免重复计算,我们需要预处理出一些关键信息。