树链剖分——重链剖分

1.树链剖分的用处

对如下问题我们可以采用树链剖分的方法去做

1.把某个节点的子树的每个节点都加上一个值z

2.查询某个节点的子树的所有节点的值的和求出来

3.把一个节点x到y之间最短路径(经过边的条数最少)上的每个节点都加上某个值z

4.把一个节点x到y之间最短路径(经过边的条数最少)上的所有节点的和求出来

由于我们需要解决这些问题,所以我们要使用树链剖分这种算法。

2.实现原理:

在这里插入图片描述

1.知识储备:

重儿子:该节点的所有儿子中,子树中节点个数最多的儿子。
举例:节点A有两个儿子,G所形成的树中有6个结点分别为 G B E V J T {GBEVJT} GBEVJT,C所形成的树中有5个结点 C F D Z X {CFDZX} CFDZX。所以A的重儿子为G。对于j结点E,它的两个儿子的大小相同,于是重儿子可以是任何一个儿子。

2.操作

我们按照每一个结点的重儿子走就形成了一条重链,如图
在这里插入图片描述

我们要找出这个图上的所有重链,如图
在这里插入图片描述
这个书上就两条重链,你找不出第3个了。
然后我们按重链的顺序为结点打上时间戳。重链按顺序打,其余的按照dfs的顺序打。如图
在这里插入图片描述

我们定义这样几个数组完成重链剖分
int
fa[N],//记录该节点的父亲
dep[N],//记录该节点的深度
son[N],// 记录重儿子
siz[N],//记录该节点子树的大小(包含该节点)
top[N],//记录该重链的顶部
dfn[N],//时间戳
w[N],//
tim;//计数器
void dfs1(int u,int f)//处理fa,dep,siz,so
{
   
    dep[u]=dep[f]+1;
    fa[u]=f;
    siz[u]=1;//当前节点的大小初始化为1(因为该节点本身算一个嘛)
    int maxx=-1;
    for(int i=head[u]; ~i; i=edge[i].next)
    {
   
        int v=edge[i].v;
        if(v==f)
            continue;//因为存的是无向图,所以一个边存两次,可能会回到该节点的父亲节点
        dfs1(v,u);
        siz[u]+=siz[v];
        if(siz[v]>maxx)
        {
   
            son[u]=v;
            maxx=siz[v];
        }

    }

}
void dfs2(int u,int t)//处理dfn,top,w,t为该链的头
    dfn[u]=++tim;
    top[u]=t;
    w[tim]=v[u];
    if(!son[u])
        return;
    dfs2(son[u],t);
    for(int i=head[u]; ~i; i=edge[i].next)
    {
   
        int v=edge[i].v;
        if(v==fa[u]||v==son[u])
            continue;
        dfs2(v,v);
    }
}

操作完成。解释一下为什么要这样做。
通过这样做我们强行把树的结点拆成一个连续的数组: A G E J T B V C F D X Z {AGEJTBVCFDXZ} AGEJTBVC

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值