树链剖分初步

树链剖分是将一棵树按照特殊的dfs序划分成链
从而使树上任意一条链最多被划分为log(n)段
同时保持的dfs序对子树操作的便利

主要用的方法是轻重链剖分:
定义size[x]为以x 为根子树的大小
son[ x ] 为 x 的儿子中size最大的节点 称为重儿子
那么x与son[ x ] 相连的边就是一条重边 其余边为轻边
重边连在一起就是重链 轻链就是轻边

这样我们就可以让尽可能多的点在dfs序中连续,这样我们修改时才会尽可能地发挥线段树区间维护的优势。

一些性质:
- 在轻边(u ,v)中 size[ u ] < size[ u ] / 2
- 从根到某一点的路径上 不超过logn条

需要维护的有:

size[ ] son [ ] 定义如上
dfn[ ] 每个点dfs序编号
dep[ ] 记录每个点深度
fa[ ] 记录每个点的father (用于以后维护dfs序上不连续区间之间的跳跃)
top[ ] 记录每个点所在链上的dep最小的点(深度最小的点)
( 用于以后维护dfs序上连续的区间
w [ ] 重新编号的每个点的权值 用线段树维护

通过两遍dfs实现:
第一遍dfs求出size son dep fa
第二遍dfs确定top dfn w

具体代码实现如下:
细节见注释

void dfs1(int x)
{
    size[x]=1;
    for(register int i=0;i<link[x].size();i++)
    {
        if(cur!=fa[x])
        {
            dep[cur]=dep[x]+1;
            fa[cur]=x;//这两个一定写在访问子节点之前
            dfs1(cur);
            size[x]+=size[cur];
            if(size[cur]>size[son[x]])son[x]=cur; //更新重儿子
        }
    }
}
void dfs2(int x,int t)
{
    p++;
    dfl[x]=p;
    top[x]=t;
    a[p]=w[x];
    if(son[x])dfs2(son[x],t);
    for(int i=0;i<link[x].size();i++)
        if(cur!=fa[x]&&cur!=son[x])
            dfs2(cur,cur);
    dfr[x]=p; //这里区间右端点可以不用记录  因为dfr=dfn+size-1 
}

这样就可以把整棵树的dfs序放在数据结构上维护

修改以及询问操作就要利用到刚刚记录的top&fa

举个栗子

栗子

假设要将(11,10)上每个点的权值加1
令x=11 , y=10
我们可以知道top[x]到x在dfs序上是连续的 top[y]到y也是一样
默认优先更新top值最大的点
那么我们可以直接更新线段树上区间(dfn[ top[y] ] , dfn[ y ])
然后y=fa[ y ]
接下来更新的是(dfn[ top[x] ] , dfn[ x ]) then x= fa[ x ]
。。。
以此类推直到top [ x ] ==top [ y ] 这时表明x y在一条链上了
那么最后更新一次(dfn [ x ] , dfn [ y ] ) (先把x深度调到 < y)
就可以收工啦
询问操作也是类似的

具体实现:

void cal(int x,int y,int z)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]]) x^=y^=x^=y;
        modi(1,n,dfl[top[x]],dfl[x],1,z);//注意要加dfn 线段树上维护是通过dfs序的编号
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) x^=y^=x^=y;
    modi(1,n,dfl[x],dfl[y],1,z);
}
int qsum(int x,int y)
{
    int ans=0;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]]) x^=y^=x^=y;
        ans+=query(1,n,dfl[top[x]],dfl[x],1);
        ans%=P;
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) x^=y^=x^=y;
    ans+=query(1,n,dfl[x],dfl[y],1);
    ans%=P;
    return ans;
}

因为剖分后依然是dfs序
那么关于x子树的修改就依然可以直接修改(dfn[ x ], dfn[x]+size[x]-1)
具体就不再赘述

值得一提的是
对于带权图可以把权值下放到边连接的深度大点上
就又变成了上面那样啦

敲代码细心噢!

稍微不注意就会找不到错

收工啦

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值