树链剖分入门

 

将树分割成多条链,然后用线段树等来维护这些链。

树链剖分的分割标准:连接一个节点的子树中的 “ 重儿子 ” // 也就是结点最多的儿子,依次连下去。

实现

定义:

重儿子:子树节点的儿子

轻儿子:除了重儿子以外的儿子

重边:父节点与重儿子组成的边

轻边:除重边以外的边

重链:重边连接而成的链

轻链:轻边连接而成的链

链头:一条链上深度最小的点

 

对于树链剖分,我们需要维护以下的数组:

名称含义
siz[u] 保存以u为根的子树节点个数
son[u] 保存u的重儿子
top[u] 保存u所在链的顶端节点
dep[u] 保存结点u的深度值
faz[u] 保存结点u的父亲节点
dfn[u] u的DFS序
rk[u] u树中的编号

 

DFS_1:预处理 siz, son, dep, faz数组

 void Dfs1(int u, int father, int depth)
 {
     dep[u] = depth;
     faz[u] = father;
     siz[u] = 1;
     
     for(int i = head[u]; i; i = edge[i].next)
     {
         int v = edge[i].to;
         // 如果连接的结点是当前结点的父亲结点,则不处理
         if(v != faz[u])
         {
             Dfs1(v,u,depth+1);
             siz[u] += siz[v];
             if(son[u] == -1 || siz[v] > siz[son[u]])
                 son[u] = v;
         }
     }
     return;
 }

DFS_2:预处理dfn, top, rk 数组。

 void Dfs2(int u, int t) // t:起始的重节点
 {
     top[u] = t;
     dfn[u] = cnt;
     rk[cnt] = u;
     cnt++;
     
     if(son[u] == -1) return;
     Dfs2(son[u], t);
     
     for(int i = head[u]; i; i = edge[i].next)
     {
         int v = edge[i].to;
         if(v != son[u] && v != faz[u])
             Dfs2(v,v);
     }
     return;
 }

维护

修改:

 void modifyOnTree(int u, int v, int val)
 {
     // 更新节点 u 到节点 v 的值
     while(top[u] != top[v])
     {
         // 不妨设dep[top[u]] > dep[top[v]]
         if(dep[top[u]] < dep[top[v]]) swap(u,v);
         Modify(1, 1, n, dfn[top[u]], dfn[u], val);
         u=faz[top[u]];
     }
     if(dep[u] > dep[v]) swap(u,v);
     Modify(1, 1, n, dfn[u], dfn[v], val);
 }

查询:

 int queryOnTree(int u, int v)
 {
     // 求和
     int res = 0;
     while(top[u] != top[v])
     {
         if(dep[top[u]] < dep[top[v]]) swap(u,v);
         res += Query(1, 1, n, dfn[top[u]], dfn[u]);
         u = faz[top[u]];
     }
     if(dep[u] > dep[v]) swap(u,v);
     res += Query(1, 1, n, dfn[u], dfn[v]);
     return res;
 }

 

原文地址:https://www.cnblogs.com/jaszzz/p/13275568.html