树链剖分

模版:

给定一棵树,有修改u,v节点路径上的信息,或修改 u 节点这颗子树的信息,或者查询链或 子树的信息 等操作。。。

可以一边操作一边查询。

日常解释用到的概念:

重儿子:父亲节点的所有儿子中子树结点数目最多(size最大)的结点;

轻儿子:父亲节点中除了重儿子以外的儿子;

重边:父亲结点和重儿子连成的边;

轻边:父亲节点和轻儿子连成的边;

重链:由多条重边连接而成的路径;

轻链:由多条轻边连接而成的路径;

 

故这里涉及到的数组有:

Hson[i] ( i 节点的重儿子是谁 )

dep[i] ( i 节点的深度)

Size[i] ( 以 i 为根节点的子树的大小 )

fa[i] ( i 节点的父亲是谁 )

dfsx[] ( 树的dfs序 )

rk[i] ( i 节点在 dfsx中的下标 )

top[i] ( i 节点所在的重链的顶节点是谁 )

 

所以首先需要一次dfs,处理 fa ,dep,Size,Hson.

紧接着是dfs2,处理 dfsx,rk,top数组。 在dfs2 中值得注意的是,先处理重儿子所在的链,保证重链的 dfs 序是连续的,这样就可以通过线段树来进行维护。

那么,对于一条链  就可以通过重链来跳跃优化,对于一个子树  它对应的 dfs 序区间就是 [ rk[i], rk[i] + Size[i] - 1 ],

贴上 dfs1 ,dfs2 的代码:

void dfs1(int p,int f){ /// 处理fa[],dep[],Size[],Hson[]
    Size[p] = 1;
    fa[p] = f;
    dep[p] = dep[f] + 1;
    for(int i = head[p]; ~i;i = edge[i].nex){
        int v = edge[i].v;
        if(v == f) continue;
        dfs1(v,p);
        Size[p] += Size[v];
        if(Size[Hson[p]] < Size[v])
            Hson[p] = v;
    }
}

int xu = 0;
void dfs2(int p,int tp){ /// 处理dfs序,top[],dfsx[]
    rk[p] = ++ xu;
    top[p] = tp;
    dfsx[xu] = p;
    if(Hson[p])         ///保证重链的dfs序连续
        dfs2(Hson[p],tp);
    for(int i = head[p]; ~i;i = edge[i].nex){
        int v = edge[i].v;
        if(v != fa[p] && v != Hson[p])
            dfs2(v,v);        //一个点位于轻链底端,那么它的top必然是它本身
    }
}

还有一点值得处理的地方,就是对于链的操作!!!

每一次对于链的操作,需要先使两个点在同一条重链上(跳的途中也有贡献),最后再来计算两个点之间的贡献。

这是点权:

/// 以线段树更新为例
void Function(int x,int y,int VAL){
    while(top[x] != top[y]){
        if(dep[top[x]] < dep[top[y]])
            swap(x,y);
        update(1,xu,rk[top[x]],rk[x],VAL,1);  /// update(l,r,ul,ur,val,rt) .  ul,ul:更新的区间
        x = fa[top[x]];
    }
    if(rk[x] > rk[y])
        swap(x,y);
    update(1,xu,rk[x],rk[y],VAL,1);
}

这是边权:

如 u,v 之间有一条边, 边权为w..  处理就是把 w 放在 u,v 中深度大的那个点。  这样就相当于每一条边的编号 都采用了 其下面那个点的编号

也就相当于,每一个节点代表的是 节点 上方的那条边。那么就可以直接用这些点做树剖。

!!!但是这样更新的时候会有一个问题。。  假如当前 u 和 v 在同一条重链上,现在要查询 u 和 v 之间的边权,直接更新 是会多更新一条边的。  所以我们需要先对 深度小的那个点 走向其重儿子

也就是这样:

/// 以线段树更新为例
void Function(int x,int y,int VAL){
    while(top[x] != top[y]){
        if(dep[top[x]] < dep[top[y]])
            swap(x,y);
        update(1,xu,rk[top[x]],rk[x],VAL,1);  /// update(l,r,ul,ur,val,rt) .  ul,ul:更新的区间
        x = fa[top[x]];
    }
    if(x == y) return ;   !!!与点权的区别
    if(rk[x] > rk[y])
        swap(x,y);
    update(1,xu,rk[Hson[x]],rk[y],VAL,1); !!!!与点权的区别
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值