树链剖分

树链剖分

树链剖分就是将一棵点权树以先遍历重儿子的方式,得到一个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;
}

 

原文地址:https://www.cnblogs.com/ganster/p/8429967.html