树分治

本文介绍了树分治算法,包括点分治和边分治两种方法。点分治通过选择树的重心作为根,统计包含根的答案,并递归处理子树。边分治则是找到一条能均衡划分树的边,统计包含边的答案,递归处理子树。两种方法各有优缺点,适用于不同的树形结构问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是分治算法:

分治算法的思想是将一个规模为N的问题分解为K个规模的较小的子问题,这些子问题相互独立且与原问题性质相同。求出这些问题的解后再将这些子问题的解合并,就可以得到原问题的解。例如快速排序和归并排序。

 

所谓树分治,就是在树形结构上的一种分治问题,主要操作是去除原本树中的某些对象,使得原树被分解成若干个互不相交的部分,由此可以解决许多以路径为询问对象的问题。

树分治分为以下两种:

基于点的分治——点分治。

基于边的分治——边分治。

 

例题1:

给你一颗包含n个对象的树和一个数K,求树上所以距离小于等于K的无序点对数目.n的范围是10^5

解题思路:

树上的路径可以分为两种:

(1)经过根的。

(2)不经过根的,即完全在子树中

 

大概流程:

(1)随意选取一个点作为根(有时候会需要选择特定点)

(2)统计包含根的答案

(3)把根去掉,递归计算子树的答案。

总体复杂度为: 递归层数*T(n)

假设极端情况,我们要处理得那个树是一条链,那么这个问题就退化了,所以我们选取的根就不能随便选了。

 

我们希望选择一个点,当删除这个点之后,结点最多的子树的结点尽量少。这个点就是树的重心

 

重心的性质:

假设当前树的重心为u,则整棵树的大小为size(u),对于u的任意儿子v有,size(v)<=size(u)/2. 

也就是说我们每次把重心删除以后,剩下的子树的规模都至少会是源树的一半。如果原来树的大小为n,那么递归的层数就会使log(n)

 

点分治小结:

1 “分”——选择树的重心作为根。

2 “治”——统计包含根的答案。

3 将根拿掉,递归算子树的答案。

void dfs(int u)
{
    int rt = 0;
    getroot(u,0);
    u = rt;  //求重心
    vis[u] = 1;
    cal(u,1,0); //计算包含根的答案
    for(int i = head[u]; ~i; i = edges[i].nxt)
    {
        int v = edges[i].to;
        if(vis[v])continue;
        cal(v,-1,edges[i].weight);//容斥
        all = sz[v];
        dfs(v);
    }
}
all = n;
dfs(1);

基于边的分治

树上的边可以分为两种:

(1)经过某条边的

(2)不经过某条边的。

大概流程:

1、选择一条边

2、统计包含这条边的答案

3、拿掉这条边,原图被分为两个不连通的子树,递归统计这两棵树的答案。

点分治选择重心,那边分治选择的是中边?

如果树的度都不超过2,则可以找到一条边,使得分得的两颗子树,较大的那个不超过2N/3

我们对于一棵不满足该前提条件的树,可以通过增加虚点来使得它满足我们的前提条件,即度不超过2

 

递归层数为log2N,(以3/2位底)。

 

边分治小结:

1、重构树形,插入虚点使得新树的度不超过2,且总点数不超过2N

2、“分”——找到一条边使得可以将树均衡地分为两部分。

3、统计包含这条边的答案。

4、递归处理两个子树

void dfs(int u)
{
    if(tot <= 1) return ;
    
    eid = pid = 0;
    getp(u,0); //得到要删除的边
    vis[eid] = vis[eid^1] = 1;
    int u = pid, v = e[eid].v;
    cal(u,v,e[eid].w,fake(eid))//统计经过这条边的答案
    
    /*递归处理*/
    int newtot = sz[v]; 
    tot = tot -= newtot;
    dfs(u);
    tot = newtot;
    dfs(v);
}
build(1);
dfs(1);

点分治时间复杂度比较优秀,但是要处理的子树可能比较多,维护起来麻烦,而且有时候还要容斥。

边分治删除边后都只剩下两颗树,并且不用容斥。但是由于需要插入虚点,所以常数比较大

 

 

 

 

 

 

 

 

 

 

 

 

 

参考:

《分治算法在树的路径问题中的应用》——长沙市雅礼中学 漆子超

【算法讲堂】【电子科技大学】【ACM】树分治入门

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值