Loading

轻重链剖分

轻重链剖分是一个比较巧妙的技术,结合了以下两种思想:

  • 链分治,即将树的结构剖分成链进行处理;
  • 将树上问题转化为序列问题(dfs 序)。

给出一些定义:

  • 重儿子:子树大小最大的一个儿子。
  • 轻儿子:不是重儿子的儿子。
  • 重边:连接重儿子的边。
  • 轻边:连接轻儿子的边。
  • 重链:重边组成的链。

如下图(不是我画的),加粗的为重边,有红点的为重儿子。

这里选择重儿子的思想类似启发式合并。从任意一个结点出发,向上每经过一条轻边都会使子树大小加倍,于是任意一点到根的路径上轻边条数为 \(O(\log n)\),即有 \(O(\log n)\) 条重链。

于是对于每条重链维护链顶 top,即可支持结点的快速上跳。作为应用,重链剖分可以支持单次 \(O(\log n)\) 查询 LCA。当两个节点在一条重链(top 相同)上时,深度小的点为 LCA。若不在一条链上,沿重链上跳即可:每次取 top 的深度小的点,跳到 top 的父亲即可。

int lca(int u,int v)
{
    while(top[u]!=top[v])//重复上跳直到在同一条重链上
    {
        if(dep[top[u]]<dep[top[v]])swap(u,v);
        u=fa[top[u]];//上跳操作
    }
    if(dep[u]>dep[v])swap(u,v);//已经在同一条重链上,深度小的为LCA
    return u;
}

在该结构的基础上引入重链优先的 dfs 序,则可方便地维护链与子树上的信息。在 dfs 时优先遍历重儿子,则此时重链上的 dfs 序连续。另外其天然满足子树 dfs 序连续,于是链与子树对应到 dfs 序上即:

  • 链:\(O(\log n)\) 个区间
  • 子树:单个区间

使用数据结构维护 dfs 序即可。最常见的为线段树,此时单次操作复杂度为 \(O(\log^{2}n)\)(瓶颈为链上操作)。

P3384 【模板】重链剖分 / 树链剖分

以模板题为例讲解实现。重链剖分过程中需要记录当前点的父亲 fa、深度 dep、子树大小 siz、重儿子 son、dfs 序 dfn 与所在重链的链顶 top,这可以通过两遍 dfs 简单求出。特别注意两次 dfs 中参数含义的不同。

void dfs1(int u,int f)//u为当前节点,f为u的父亲
{
    fa[u]=f;siz[u]++;dep[u]=dep[f]+1;
    int maxsiz=0;
    for(int i=h[u];i;i=e[i].nxt)
    {
        int p=e[i].to;
        if(p==f)continue;
        dfs1(p,u);siz[u]+=siz[p];
        if(maxsiz<siz[p]){maxsiz=siz[p];son[u]=p;}
    }
}
void dfs2(int u,int f)//u为当前节点,f为u所在链的链顶
{
    top[u]=f;dfn[u]=++dfncnt;
    if(!son[u])return;
    dfs2(son[u],f);
    for(int i=h[u];i;i=e[i].nxt)
    {
        int p=e[i].to;
        if(p!=son[u]&&p!=fa[u])dfs2(p,p);
    }
}

子树的加与修改是直接的:

void addtree(int u,int k)
{
	modify(1,dfn[u],dfn[u]+siz[u]-1,k%p);
}
int querytree(int u)
{
	return query(1,dfn[u],dfn[u]+siz[u]-1);
}

链上的操作在 LCA 处已经有所展现。本题中加法这一信息的性质较好,无需考虑交换律的问题,写起来较为简单。

void addpath(int u,int v,int k)
{
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]])swap(u,v);
        modify(1,dfn[top[u]],dfn[u],k%p);
        u=fa[top[u]];
    }
    if(dep[u]>dep[v])swap(u,v);
    modify(1,dfn[u],dfn[v],k);
}
int querypath(int u,int v)
{
    int ans=0;
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]])swap(u,v);
        ans+=query(1,dfn[top[u]],dfn[u]);ans%=p;
        u=fa[top[u]];
    }
    if(dep[u]>dep[v])swap(u,v);
    ans+=query(1,dfn[u],dfn[v]);ans%=p;
    return ans;
}
posted @ 2021-07-12 12:42  pjykk  阅读(170)  评论(0)    收藏  举报