zoukankan      html  css  js  c++  java
  • 树链剖分

    树链剖分

    English speaker can click here.

    该文章难度较浅,一些巨佬们可自行忽视

    引入

    树链剖分就是把一棵树切成很多根链,那怎么去剖呢?这是有讲究的。

    明确一些定义

    重儿子:一个根节点下子树最大的那个儿子。

    轻儿子:除了重儿子的所有儿子。

    重边:与重儿子相连的边。

    轻边:与轻儿子相连的边

    重链:全部由重边组成的链

    几条结论

    内容

    这是最关键的:任一节点到根节点到经过的轻边不超过logN

    还有几条比较水的:

    1.每一个节点最多属于一条重链

    2.重链不相交

    证明:

    只证第一条:

    从任一节点经过轻边到达上一层,经过一次轻边子树的个数会翻上1倍,所以容易得知一定少于logN。
    其余的感性认识。

    核心

    数链剖分的本质就是讲一颗树分成若干个链的组合,从而做到将树状结构的问题转化到了线性结构。

    预处理

    首先呢,我们需要维护出每个节点的深度和他属于那个重链。

    显而易见,为了维护重链,我们还需要知道每个节点对应的子树的大小。

    当然,要想把树转换成线性结构,一个dfs序是很重要的,而且还要保证每一条重链上的dfs序是连续的。

    所以我们肯定不能在一次dfs中完成以上的所有事情,所以两遍dfs是很重要。

    关于,如何维护,每个人都有自己的做法。我提供我的伪代码。

    #include <bits/stdc++.h>
    using namespace std;
    struct Node
    {
        int fa;// 父亲是谁? 
        int dep;//我的传承有多长? 
        int pos;//我的重边走向何方? 
        int heavy;//我的重链家在何处? 
        int size;// 我子子孙孙有多少? 
        int ds;//dfs序 
    }tr[MAXN];
    vector<int>vec[MAXN];
    void dfs1(int now,int fa)// 我想知道我爹和我失散多年的重儿子 
    {
        tr[now].size = 1;
        tr[now].dep = tr[fa].dep+1;
        int mx = 0 ;
        for(int i = 0;i < vec[now].size();i++)
        {
            if(vec[now][i] != fa)
            {
                dfs1(vec[now][i],now);    
                tr[now].size += tr[vec[now][i]].size;
                if(tr[vec[now][i]].size > mx)
                {
                    mx = tr[vec[now][i]].size;
                    tr[now].pos = vec[now][i];
                }
            }    
        } 
    }
    int tot = 0;
    void dfs2(int now,int fa)
    {
        tr[now].ds = ++ tot;
        if(tr[fa].pos == now) 
        {
            tr[now].heavy = tr[fa].heavy;
        }
        dfs2(tr[now].pos,now);
        for(int i = 0;i < vec[now].size();i++)
        { 
            if(vec[now][i] != fa && vec[now][i] != tr[now].pos)
            {
                dfs2(vec[now][i],now);
            }
        }
    } 

    到这里,其实树链剖分的核心已经完成了。

    线性处理

    一般来说,传统意义上的树链剖分是会套上线段树,如果套上了splay的话,那么就会被称为LCT。

    而线段树处理是,对于轻边链接的点我们使用单点修改,而重链则是直接跳到重链的顶端,重链位于一段连续的区间,所以,可以用区间维护。

    这样的话因为我们的前置定理,所以树剖部分的复杂度最多为logN。

    关于

    其实树剖本身并不是一个完整的算法,其本质是用一些可以用在线性结构上的结构可以在多花费logN的前提下,去做树状结构的题

  • 相关阅读:
    (十五)、常见的几种RuntimeException
    (十四)、泛型中extends和super的区别
    (十三)、Java泛型
    (十二)、构造方法、静态属性和静态方法的使用要点
    (十一)、final,finally,finalize的区别
    (十)、java内部类与内部类的闭包和回调
    (九)、线程sleep和wait的区别
    (八)、java中==和equals和hashCode的区别
    sketch中做outline icon的制作技巧
    sketch Measure的安装及使用
  • 原文地址:https://www.cnblogs.com/mzyy1001/p/11205671.html
Copyright © 2011-2022 走看看