博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
树链剖分总结
阅读量:5955 次
发布时间:2019-06-19

本文共 3940 字,大约阅读时间需要 13 分钟。

树链剖分总结

基本思想

对于有些题目,我们往往需要对一些树链进行修改或者查询,而我们不可能每次都暴力修改暴力枚举,这样做的时间复杂度很高,而很多有修改和查询操作的数据结构例如线段树和树状数组又不能在树上进行,这个时候,我们就引进了一种算法,树链剖分,将一颗树划分为若干条链,再用数据结构去维护。

树剖的一些基础概念

重儿子和轻儿子

重儿子指的是对于一个节点来说,他的儿子中\(size\)最大的那一个为他的重儿子(若有多个则任取一个),其它的兄弟儿子为轻儿子(下面的重节点与轻节点跟这里一样)

重边和轻边

一个节点与它的重儿子的连边叫重边,与轻儿子的连边叫轻边。

重链

连续的重边连接起来就是一条重链

链顶

一个节点所在重链的深度最小的节点为链顶,轻节点的链顶为自身(一般用于跳链)

[][1]

[1]:
上图中带红点的节点为轻节点,加粗的边为重边,而连续重边就是一条重链,例如1 4 9 13 14构成一条重链,3 7构成一条重链,2 6 11构成一条重链。

性质

一条树链上重链的条数最多为log条

因为每跳一次轻边都会至少将\(size*2\)
而重链一跳就跳到顶

实现过程

一般来说树剖需要两遍dfs进行预处理,第一遍求出每个节点的重儿子,父亲,第二遍构建重链并求出每个节点所在重链的顶端,如果不是重链则顶端为自己,并且将每个点重新编号,按dfs序编号,每次优先走重边,这样重边所连的子树的所有节点dfs序一定小于轻边所连的子树,且重链上节点的新编号一定是连续的。

void dfs1(int f,int u){    fa[u]=f;    size[u]=1;    for(int i=h[u],s=0;i;i=nex[i])    {        int v=to[i];        if(v==f)continue;        dfs1(u,v);        size[u]+=size[v];        if(size[v]>s)s=size[v],son[u]=v;    }}int dfs2(int u){    dfn[u]=++cnt;    int x=dfn[u];    a[x]=aa[u];    if(!top[u])top[u]=u;    if(son[u]){top[son[u]]=top[u];dfs2(son[u]);}    for(int i=h[u];i;i=nex[i])    {        int v=to[i];        if(v==son[u]||v==fa[u])continue;        x=max(x,dfs2(v));    }    return x;}

修改与询问

修改与询问其实差不多,原数据结构几乎可以照搬,按我们上述的方法进行重新编号之后会发现,每一条重链上的节点或者每一棵子树上点节点编号都是连续的。然后我们会发现,对两点间最短路的操作其实就是对两点间每条重链的一个区间进行操作,不断的跳链,再不断维护跳过即可,以修改操作为例

while(top[x]!=top[y])//跳链的前提是两点不在同一条重链上    {        if(dfn[x]
dfn[y])swap(x,y); update(1,1,n,dfn[x],dfn[y],z);/*此时两点已经跳到同一条重链上,但这条重链上的被包含的区间还没有维护,继续维护。*/

我们可以看到事实上树链剖分的作用就是将所需维护的区间找出来,所有的维护操作与原数据结构一样,不需要任何修改,查询操作与修改基本相同。

总结

树链剖分是一种让线性数据结构能够在树上进行维护的一种算法,时间复杂度为原数据结构的复杂度*log。

代码(以线段树为例)

题目来源(洛谷P3384【模板树链剖分)

#include
#define ll long long#define lc (no<<1)#define rc (no<<1|1)#define ls lc,l,mid#define rs rc,mid+1,rusing namespace std;inline int gi(){ char a=getchar();int b=0; while(a<'0'||a>'9')a=getchar(); while(a>='0'&&a<='9')b=b*10+a-'0',a=getchar(); return b;}const int N=1e6+20;ll aa[N],a[N],h[N],to[N],nex[N],dfn[N],fa[N],son[N],top[N],size[N],t[N],lazy[N],n,m,root,p,cnt;void dfs1(int f,int u){ fa[u]=f; size[u]=1; for(int i=h[u],s=0;i;i=nex[i]) { int v=to[i]; if(v==f)continue; dfs1(u,v); size[u]+=size[v]; if(size[v]>s)s=size[v],son[u]=v; }}int dfs2(int u){ dfn[u]=++cnt; int x=dfn[u]; a[x]=aa[u]; if(!top[u])top[u]=u; if(son[u]){top[son[u]]=top[u];dfs2(son[u]);} for(int i=h[u];i;i=nex[i]) { int v=to[i]; if(v==son[u]||v==fa[u])continue; x=max(x,dfs2(v)); } return x;}void bt(int no,int l,int r){ if(l==r){t[no]=a[l];return;} int mid=(l+r)>>1; bt(ls);bt(rs); t[no]=t[lc]+t[rc];}void pushdown(int no,int l,int r,int k){ t[no]+=k*(r-l+1); if(l!=r)lazy[lc]+=k,lazy[rc]+=k; lazy[no]=0;}void update(int no,int l,int r,int L,int R,int k){ if(l>=L&&r<=R){lazy[no]+=k;pushdown(no,l,r,lazy[no]);return;} if(lazy[no])pushdown(no,l,r,lazy[no]); if(r
R)return; int mid=(l+r)>>1;update(ls,L,R,k);update(rs,L,R,k); t[no]=t[lc]+t[rc];}ll query(int no,int l,int r,int L,int R){ if(lazy[no])pushdown(no,l,r,lazy[no]);lazy[no]=0; if(l>=L&&r<=R)return t[no]%p; if(l>R||r
>1; return (query(ls,L,R)+query(rs,L,R))%p;}int main(){ n=gi(),m=gi(),root=gi(),p=gi(); for(int i=1;i<=n;++i)aa[i]=gi(); for(int i=1,t=0;i
dfn[y])swap(x,y); update(1,1,n,dfn[x],dfn[y],z); } if(op==2){ int x=gi(),y=gi();int s=0; while(top[x]!=top[y]) { if(dfn[x]
dfn[y])swap(x,y); s+=query(1,1,n,dfn[x],dfn[y]); printf("%lld\n",s%p); } if(op==3){ int x=gi(),z=gi(); update(1,1,n,dfn[x],dfn[x]+size[x]-1,z); } if(op==4){ int x=gi(); printf("%lld\n",query(1,1,n,dfn[x],dfn[x]+size[x]-1)); } }}

转载于:https://www.cnblogs.com/ljq-despair/p/8639462.html

你可能感兴趣的文章
Android开发学习总结(五)——Android应用目录结构分析(转)
查看>>
[PHP]PHP rpc框架hprose测试
查看>>
Atom 编辑器系列视频课程
查看>>
C#三种定时器
查看>>
范数 L1 L2
查看>>
协同过滤及大数据处理
查看>>
Java8 本地DateTime API
查看>>
jQuery 增加 删除 修改select option
查看>>
[原][osgearth]osgearthviewer读取earth文件,代码解析(earth文件读取的一帧)
查看>>
springboot 常用插件
查看>>
一个基于特征向量的近似网页去重算法——term用SVM人工提取训练,基于term的特征向量,倒排索引查询相似文档,同时利用cos计算相似度...
查看>>
[转]Newtonsoft.Json高级用法
查看>>
35个Java代码性能优化总结
查看>>
Spring+SpringMVC+MyBatis+easyUI整合基础篇(一)项目简述及技术选型介绍
查看>>
DFI、DPI技术
查看>>
hibernate 执行存储过程 方法
查看>>
RapidIOIP核的验证方法研究_王玉欢
查看>>
崩溃日志的实例
查看>>
base64是啥原理
查看>>
字符串中去除连续相同的字符保留一个
查看>>