📜  树的质心分解(1)

📅  最后修改于: 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)$。

总结

质心分解是一种十分实用的树的分治算法,被广泛应用于众多树上的问题。需要注意的是,在实现时要使用递归方式,同时为了避免重复计算,我们需要预处理出一些关键信息。