树链剖分
树链剖分就是将一棵点权树以先遍历重儿子的方式,得到一个dfs序,再对这个dfs序进行操作,与一般的dfs序不同,重链在该dfs序是一个连续不断的区间,该dfs序支持链修改,链查询,子树查询,子树修改,求lca等。在一棵有根树上,我们称一个节点u 的子节点v 为节点u 的重儿子当且仅当v 是所有子节点中大小最大的(如果有多个,则随便选定一个),其余节点称为轻儿子。如果我们对于所有内部节点(有儿子的节点)都选择一个节点作为其重儿子,那么我们就得到了一个轻重链划分,我们称连接节点及其重儿子的边为重边,反之则为轻边,一条由重边组成的链称为重链。
要定义的的东西:
father[ N ] :表示一个节点的父亲节点;
in[ N ] :表示一个节点进dfs序的位置;
out[ N ] :表示一个节点出dfs序的位置;
top[ N ] :表示一个节点只走重链能到达的最高位置;
son [ N ] :表示一个节点的重儿子;
siz[ N ] :表示一个节点的大小(其子树中节点数加上自己);
dep[ N ] :表示一个节点的深度;
代码:
void dfs1( int u ) { siz[u] = 1; for( int t = head[u]; t; t = last[t] ) { int v = dest[t]; if( v == fat[u] ) continue; fat[v] = u; dep[v] = dep[u]+1; dfs(v); siz[u] += siz[v]; if( siz[v] > siz[son[u]] ) son[u] = v; } } dep[1] = 1; fat[1] = 1; dfs1(1);
void dfs2( int u, int tp ) { in[u] = ++idc; top[u] = tp; if( son[u] ) dfs2( son[u], tp );//遍历重链 for( int t = head[u]; t; t = last[t] ) {//遍历轻边 int v = dest[t]; if( v == fat[u] || v == son[u] ) continue; dfs2( v, v ); } } idc = 0; dfs2(1,1);
求lca:
int lca( int u, int v ) { while( top[u] != top[v] ) { if( dep[top[u]] < dep[top[v]] ) swap(u,v); u = fat[top[u]]; } return dep[u] < dep[v] ? u : v; }