树链剖分--利用数据结构维护一棵树

我理解的树链剖分就是将一棵树剖分成若干条链,然后利用数据结构取维护每一条链,复杂度为log(n)

---------------------------------树链剖分对轻重边的划分-------------------------------------------------------------------

定义size(x)为以x为根的子树的节点数.

令v为u的儿子中size最大的节点,那么(u,v)就是重边,其余边为轻边.

那么如此划分,可以得到两条性质:1.轻边(u,v)中,size(v) <= size(u)/2.

                  因为轻边所连的儿子的size一定不是最大的,所以它最少有一个比它大的兄弟,所以该条性质成立

                                                                2.从根到某一点的路径上,不超过log(n)条轻边和不超过log(n)条重路径

                  因为只有当前节点的儿子数不少于2,才可能出现轻边,出现轻边轻边,才可能出现新的重链,因为二叉树的最大深度logn,所以轻边的数量不可能超过这个深度.

------------------------------算法流程------------------------------------------------------------------------------------------

两次dfs: 1.第一次dfs找出所有的重边并将重边标记

     2.dfs链接重边形成重链.具体过程:以根节点为起点,沿着重边向下拓展,拉成重链,不在当前重链上的节点,都以该节点为起点向下重新拉一条重链

每条重链相当于一段区间

剖分过程如下,比较容易理解:

#include <iostream>
#include <cstring>
#include <algorithm>
#define MAX 100007

using namespace std;

int siz[MAX]; // 用来保存以x为根的子树的节点个数
int top[MAX];//用来保存当前节点所在链的顶端节点
int son[MAX]; //用来保存重儿子
int fa[MAX]; //用来保存当前节点的父亲
int dep[MAX];//用来保存当前节点的深度
int tid[MAX];//用来保存一棵树中剖分后的新编号
int rank[MAX];//tid的反函数
int tim = 0;

int head[MAX];
struct
{
    int v,next;
}e[MAX];

//第一次dfs标记重边
void dfs1 ( int u , int father , int d ) //三个参数分别是当前节点u,当前节点的父亲father,当前的深度d
{
    dep[u] = d; fa[u] = father ; siz[u] = 1;
    for ( int i = head[u] ; ~i ; i = e[i].next )
    {
        int v =e[i].v;
        if ( v == father ) continue;
        dfs1 ( v , u , d+1 );
        siz[u] += siz[v];
        if ( son[u] == -1 || siz[v] > siz[son[u]] ) son[u] = v ;
    }
}

//第二次dfs:连重边成重链
void dfs2 ( int u , int tp )
{
    top[u] = tp; tid[u] = ++tim;
    rank[tid[u]] = u;
    if ( son[u] == -1 ) return;//当前节点为叶节点
    dfs2 ( son[u] , tp );
    for ( int i = head[u] ; ~i ; i = e[i].next )
    {
        int v = e[i].v;
        if ( v == son[v] || v == fa[u] ) continue;
        dfs2 ( v , v );
    }
}

int main ( )
{

}

然后就是讲为什么这么剖分?剖分完后,每条重链相当于一段区间,可以用数据结构去维护.

把所有重链首尾相接,放到同一个数据结构中,然后维护这一整体即可.

------------------------------修改操作-------------------------------------------------------------------------------

1.最简单的单点修改

直接根据节点新的标号在数据结构中修改即可

2.整体修改点u到点v的路径上的权值

1)如果u和v在同一条重链上,直接用数据结构修改tid[u]至tid[v]间的值.

2)如果u和v不在同一条重链上,一边进行修改,一边将u和v往同一条重链上靠,然后就转化为了情况1.

   a)若fa[top[u]]与v在同一条重链上,说明两条重链之间有一条轻边相连,那么直接修改u到top[u]间各权值,然后u跳至fa[top[u]],转化为情况1

            b)若u向上经过若干条重链和轻边与v在同一条重链上,不断修改u和top[u]间的各权值,再将u跳转至fa[top[u]],直至u和v在同意条重链上

   c)若u和v都是向上经过若干条重链和轻边,到达同一条重链.每次在点u和点v中,选取dep[top[x]]中较大的x,修改x与top[x]间的各权值,再跳至fa[top[x]],直到u和v在同一条重链上.





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值