树链剖分

算法问题描述

在树上的节点间的路径上进行各种操作

算法思想

对于序列区间,我们有强有力的工具——线段树,但对于树这种结构本身并不是一长条链,但我们可以人为剖分出一条一条链来进行区间维护,最后合并这一条条链就得到树上区间的答案了。接下来的问题是如何剖分,显然对于随机数据我们随机剖分的复杂度应该是满足logn的性质的,但通常数据会可以编造,所以我们需要启发式剖分。

启发式剖分

每个节点向自己孩子最多的子节点连重边其他连轻边,最后图中一定有多条重边连成的重链,重链在线段树中放在一起做区间处理,其他的按深搜序放,这样的好处的一个节点的子树在线段树上就是连续的了,可以附带支持子树操作

复杂度口糊

考虑每个非叶子节点只会有一个重边,重边是能加速操作的,而那些轻边只能单独存在地操作,所以轻边越多复杂度越高,但是每个节点轻边多意味着每个节点的孩子多,意味着整棵树的层数小,树上路径短,所以综合想象一下这样剖分的复杂度还是挺O98K的

代码

work是各种操作,如query,update等

void build(int o,int f,int r){
    tr[o]=(TREE){f,r,++tot};
    if(f==r){
        tr[o].w=w[rnk[f]];
        return;
    }
    build(o<<1,f,mid);
    build(o<<1|1,mid+1,r);
    pushup(o);
}
void work(int x,int y){return;}//略
void DFS1(int u){
    size[u]=1;
    dep[u]=dep[fa[u]]+1;
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].v;
        if(dep[v]) continue;
        fa[v]=u;
        DFS1(v);
        size[u]+=size[v];
        if(size[v]>size[son[u]] || !son[u]) son[u]=v;
    }
}
void DFS2(int u,int tp){
    top[u]=tp;
    tid[u]=++tim;
    rnk[tim]=u;
    if(!son[u]) return ;
    DFS2(son[u],tp);
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].v;
        if(v==fa[u] || v==son[u]) continue;
        DFS2(v,v);
    }
}
void work_path(int x,int y){
    int fx=top[x],fy=top[y];
    while(fx!=fy){
        if(dep[fx]<dep[fy]) swap(x,y),swap(fx,fy);
        work(1,tid[fx],tid[x]);
        x=fa[fx];
        fx=top[x];
    }
    if(dep[x]>dep[y]) swap(x,y);
    work(1,tid[x],tid[y]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jarden_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值