基本思想
树链剖分一种对树进行划分的算法,它先通过轻重边剖分将树分为多条链,保证每条边属于且只属于一条链,然后再通过数据结构来维护每一条链。
一些定义
树链:树上的路径.
剖分:把路径分类为重链和轻链.
重儿子:u的子节点中siz[v]值最大的v.
轻儿子:u的其它子节点.
重边:点u与其重儿子的连边.
轻边:点u与其轻儿子的连边.
重链:由重边连成的路径.
轻链:轻边.
性质
- 如果(u,v)为轻边,则siz[v]$ imes$2<siz[u].
- 从根到某一点的路径上轻链、重链的个数都不大于log$;$n.
- 树剖序其实也可以是dfs序的一种.
实现
一些变量:
f[u]表示u的父亲.
siz[u]表示以u为根的子树的大小.
dep[u]表示u的深度(根深度为1).
top[u]表示u所在的链的顶端节点.
son[u]表示与u的重儿子.
重标号:
p[u]:重标号后u的编号.
dfs序:dfs的时候先走重边.
这样可以使得重边的编号是连续的,方便维护.
- 用两遍dfs求出所需的所有变量以及重标号.
int f[N],p[N],dep[N],siz[N],son[N],top[N]; /*top[u]:u所在的链的顶端节点,son[u]:u的重儿子*/ inline void dfs1(int u){ int m=0;siz[u]=1; for(int i=g[u];i;i=e[i].nxt) if(!dep[e[i].to]){ f[e[i].to]=u; dep[e[i].to]=dep[u]+1; dfs1(e[i].to); siz[u]+=siz[e[i].to]; if(siz[e[i].to]>m){ son[u]=e[i].to; m=siz[e[i].to]; } } } inline void dfs2(int u,int tp){ top[u]=tp;p[u]=++cnt;ww[cnt]=w[u]; if(son[u]) dfs2(son[u],tp); for(int i=g[u];i;i=e[i].nxt){ if(e[i].to!=f[u]&&e[i].to!=son[u]) dfs2(e[i].to,e[i].to); } }
- 访问修改(u,v):
类似倍增的走法,每次将深度大的往上移,直到u,v属于同一条链.
inline int sum(int x,int y){ int ret=0,t; while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]]){ t=x;x=y;y=t; } ret+=ask(1,p[top[x]],p[x]); x=f[top[x]]; } if(p[x]>p[y]){ t=x;x=y;y=t; } ret+=ask(1,p[x],p[y]); return ret; }
inline void change(int x,int y,int k){ int t; while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]]){ t=x;x=y;y=t; } cover(1,p[top[x]],p[x],k); x=f[top[x]]; } if(p[x]>p[y]){ t=x;x=y;y=t; } cover(1,p[x],p[y],k); }