算法详解——树分治

树分治是一种将分治策略应用于树形结构的算法,通过找根节点(重心)减少分治层数。文章介绍了如何寻找根节点,以及树分治的基本思想,将树的每个节点视为部门,逐层分发任务。还提及了树分治在 poj1741 和 bzoj2152 例题中的应用。
摘要由CSDN通过智能技术生成

树分治

一句话讲,把分治做到树上。

树分治首先要把无根树转成有根树(如果是无根树,当然有根树就直接分治),即找一个点 Root R o o t 作为根。

如何找根?

为了分治的时效,我们需要分治的层数越少越好,于是想让找到的根下最大子树的节点越少越好,我们便可以用一趟 dfs d f s 来刷。
这里需要了解几个数组: Fi F i 表示 i i 节点下最大子树的节点数,Sizei表示 i i 节点所在子树的节点数。
我们在遍历到j这个点时, Sizefaj+=Sizej S i z e f a j + = S i z e j Ffaj=max{Sizej} F f a j = m a x { S i z e j } 。因为用 dfs d f s ,所以逆推就好了。

void getrt(int x,int fa){ //找根
    F[x]=0;Size[x]=1; //初始化
    for (int i=lnk[x];i;i=nxt[i]){ //遍历下一个点
        if (vis[son[i]]||son[i]==fa) continue; //防止循环
        getrt(son[i],x); //dfs下去
        Size[x]+=Size[son[i]];
        F[x]=max(F[x],Size[son[i]]); //修正
    }
    F[x]=max(F[x],Sum-Size[x]); //Sum是整棵树的节点数,也就是Sum=N
    if (F[x]<F[Root]) Root=x; //修改Root
}

注意,代码中有一句F[x]=max(F[x],Sum-Size[x]);,是因为 x x 节点没法遍历到fa节点,但是以 x x 为根,fa节点所在的子树没有被统计,所以最后要补上一道。
PS:所求的点又叫重心

正题

现在进入正题——树分治
我们可以把树上的每个节点看成一个人,整棵树看成一个公司,老板(根节点)有一项任务,于是找来了所有部门,把任务下发;而每个部门又可以看成一棵树,部长(子树的根节点)找来了该部门的所有员工,把任务下发,以此类推。这样就是分治。
那么接下来就很简单了。从根节点开始,下面每个节点找一下重心,继续分治下去即可。

inline void Solve(int x){ //分治
    vis[x]=1;
    for (int i=lnk[x];i;i=nxt[i]){
        if (vis[son[i]]) continue; //已经分过了就不要再分,防止循环
        Root=0;Sum=Size[son[i]]; 
        //初始化,这里Sum=Size[son[i]],是因为这棵子树从整棵树上摘下来,也就是公司不管部门的事
        getrt(son[i],0); //找子树的重心
        Solve(Root); //继续分下去
    }
}

好了,没了,就这么简单。

例题

poj1741 Tree
bzoj2152聪聪可可 双倍经验
洛谷

还有一种边分治,但我这种蒟蒻555

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值