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的前提下,去做树状结构的题

  • 相关阅读:
    Working with macro signatures
    Reset and Clear Recent Items and Frequent Places in Windows 10
    git分支演示
    The current .NET SDK does not support targeting .NET Core 2.1. Either target .NET Core 2.0 or lower, or use a version of the .NET SDK that supports .NET Core 2.1.
    Build website project by roslyn through devenv.com
    Configure environment variables for different tools in jenkins
    NUnit Console Command Line
    Code Coverage and Unit Test in SonarQube
    头脑王者 物理化学生物
    头脑王者 常识,饮食
  • 原文地址:https://www.cnblogs.com/mzyy1001/p/11205671.html
Copyright © 2011-2022 走看看