树剖教程

【序言】我可是用树链剖分做的= =树分治写起来更麻烦。树剖我今天刚刚入门= =,写一篇题解加深印象。
因为脑中还残存着的没学之前的一些想法,本教程更适合初学者吧。希望各路大牛也多多指导。
以下内容结合一篇讲的不错的教程,我加了一些改动,更加易懂吧。也请原创大牛释怀 = =。
【问题】在一棵树上进行路径的修改、求极值、求和。
【树链剖分的概念】树链,就是树上的路径。剖分,就是把路径分类为重链和轻链。树链剖分就是把一些点合成一条路径,使其在线段树中的编号(下标)有序,并用线段树来维护,使得查询、修改的效率大大提高(有点像莫队的分块思想)。假设我们把路径分好链了(先不要在乎是怎么分的),每次询问两个点对(x,y)时,若x和y在同一链中,直接询问线段树中的u和v(因为同一条链中下标是连续的)u,v是x,y对应的线段树中的点。否则的话,我们从深度大的点上一点一点向上爬,每次记录该点所在的链上的情况,直到x,y在同一条链上。
【注意】树链剖分中的线段树中每个点代表的意义可以是原图的边或点。这道题是点,我就以点来叙述。
【数组含义简介】记num[v]表示以v为根的子树的节点数,deep[v]表示v的深度(根深度为1),top[v]表示v所在的链的顶端节点,f[v]表示v的父亲,son[v]表示与v在同一重链上的v的儿子节点(姑且称为重儿子),tree[v]表示节点v在线段树中的编号,pre[v]表示线段树中编号是v的节点所对应的原图中的点(与tree相反)
只要把这些东西求出来,就能用logn的时间完成原问题中的操作。
【术语解释】
重儿子:num[u]为v的子节点中num值最大的,那么u就是v的重儿子。
轻儿子:v的其它子节点。
重边:点v与其重儿子的连边。
轻边:点v与其轻儿子的连边。
重链:由重边连成的路径。
轻链:轻边。
剖分后的树有如下性质:
性质1:如果(v,u)为轻边,则siz[u] * 2 < siz[v];
性质2:从根到某一点的路径上轻链、重链的个数都不大于logn。
至于证明吧,我就不证了~~(其实我不会)只要能应用、并知道复杂度就行了。
【预处理算法实现】
我们可以用两个dfs来求出fa、deep、num、son、top、tree、pre。
dfs_1:把fa、deep、num、son求出来,比较简单。
dfs_2:①我们依次标记tree[v](按搜索的顺序),同时得到pre。
②对于v,当son[v]存在(即v不是叶子节点)时,显然有top[son[v]] = top[v]。(没有就退出)
③然后我们先搜索v的重儿子u,并把u的重儿子、重孙子…的top值也置为top[v];
④接着我们再搜索v的轻儿子u,并把u的重儿子、重孙子…的top值置为u;
将树中各边的权值在线段树中更新,建链和建线段树的过程就完成了。
【查询&修改算法实现】
例如将u到v的路径上每条边的权值都加上某值x。
记f1=top[u],f2=top[v]。
当f1<>f2时:不妨设dep[f1]>=dep[f2],那么就更新u到f1的权值(logn),并使u=f[f1]。
当f1=f2时:u与v在同一条重链上,直接更新u到v路径上的点的权值(logn),修改完成;
重复上述过程,直到修改完成。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值