点分治

Posted guoshaoyang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了点分治相关的知识,希望对你有一定的参考价值。

 点分治

点分治是树上分治的一种(树上分治还有边分治),常用于解决和树上路径有关的问题。

因为树上路径有一条性质:树上的任何路径,要么经过根节点$rt$要么就全部在$rt$的一颗子树上。

正确性显而易见:树上两点的路径是唯一的,如果两点在$rt$的同一子树上,则路径完全在一颗子树上,如果在$rt$的不同子树,则必然经过$rt$。

有了这条性质,我们就可以对树上的路径进行分治:先将经过$rt$的路径处理完,此时$rt$的各个子树上的路径就互不影响了,故可以递归分治。

但怎么使得分治均匀呢?如果随意选择根节点,则如果树退化成链,则递归层数为$N$,且每次操作的节点数目都会非常多。

所以此时,我们选择树的重心,这样可以使每一次分治后剩下的最大子树的大小下降最快(重心的最大子树最小),每一次分治我们都找重心,操作本身复杂度为$O(NlogN)$,可以使分治底层执行次数降到$O(NlogN)$(主定理)。通常情况下,节点的子树超过$2$棵,则复杂度往往会低于$O(NlogN)$

技术图片
void getrt(int x,int fa)
    siz[x]=1;
    maxs[x]=0;
    for(int i=head[x];i;i=nxt[i])
        if(fa==to[i]&&vis[to[i]])
            continue;
        getrt(to[i],x);
        siz[x]+=siz[to[i]];
        maxs[x]=max(maxs[x],siz[to[i]]);
    
    maxs[x]=max(maxs[x],sum-siz[x]);
    if(maxs[x]<maxs[rt])
        rt=x;
求树的重心代码(顺便更新根节点)

 

这有什么用呢?

举个例子,如果我们要统计一棵树内各个长度的路径的个数,就可以用点分治来做。

首先,我们将树的重心设为根,求出树上各点到根的距离($O(N)$),并统计每个长度的路径的数量($O(N^2)$),然后枚举每一个子树,递归执行同样的操作,并在同一个数组上统计数量

 

以上是关于点分治的主要内容,如果未能解决你的问题,请参考以下文章

基于点分治的树分治

POJ 1741 Tree ——点分治

bzoj2599 [ IOI2011] -- 点分治

算法有关点分治的一些理解与看法

POJ 3714 分治/求平面最近点对

分治——最近点对