长链剖分 入门

长链剖分

额,其实和树剖差不多,对于每个节点 \(u\) 维护 \(mxd_u\) 为子树内节点深度最大值。

那么令 \(Son(u)\) 里取到 \(mxd_v\) 最大的儿子 \(v\) 为长儿子,类似重链剖分处理即可。

同样令连接不同长链的两个点之间的边为虚边。

有如下性质:

  • 从根到节点 \(u\),所经过虚边个数不超过 \(O(\sqrt n)\),这是因为从 \(u\) 开始往上走,所走到的每一条长链其长度都是严格长于当前长链的,即使 \(mxd\) 相同,往上走的那条链至少还会加上父亲,这告诉我们走到根,所有的长链最多也就是 \(1+2+3……\le n\)
  • 每条链底是叶子,且是子树内最深节点
  • 节点 \(u\)\(k\) 级祖先所在长链长度不小于 \(k\)

\(O(n\log n)-O(1)\) 树上 \(k\) 级祖先

我们先利用倍增预处理出 \(f_{u,i}\)\(u\)\(2^i\) 级祖先,\(lg_i=\lfloor\log_2(i)\rfloor\)

查询 \(u\)\(k\) 级祖先,先查 \(x=f_{u,lg_k}\)\(k'=k-2^{lg_k}\)

这样就满足 \(x\) 所在长链长度 \(>k'\)

我们对于每个链,维护从链顶往上走 \(0\sim len\) 和往下走 \(0\sim len\) 的节点,就可以分类讨论 \(dep_x-dep_{top_x}\)\(k'\) 的大小关系找到 \(k\) 级祖先了

在存储时,我们可以类似树剖进行 \(dfn\) 重标号,将 \(u\) 存下 \(top_u\) 往上走 \(dep_u-dep_{top_u}\) 步的节点。

void dfs(int u,int fa){
    f[u][0]=fa;dep[u]=dep[fa]+1;mxd[u]=dep[u];
    for(int i=1;i<=19;++i)f[u][i]=f[f[u][i-1]][i-1];
    for(auto v:e[u])if(v!=fa){
        dfs(v,u);if(mxd[v]>mxd[u])son[u]=v,mxd[u]=mxd[v];
    }
}
void dfs2(int u,int tp,int lt){
    dfn[u]=++idx;down[idx]=u;up[idx]=lt;top[u]=tp;
    if(!son[u])return ;
    dfs2(son[u],tp,f[lt][0]);
    for(auto v:e[u])if(v!=f[u][0]&&v!=son[u])dfs2(v,v,u);
}
int get(int x,int k){
    if(!k)return x;
    x=f[x][lg[k]];
    k-=1ll<<lg[k];
    int d=dep[x]-dep[top[x]];
    if(d>=k)return down[dfn[x]-k];
    return up[dfn[top[x]]+k-d-1];
}

优化 dp

长链剖分可以优化 维护仅与深度有关的一个信息 的 dp。

例如子树内深度为 \(k\) 的节点个数/子树内深度出现次数众数等。

一般形式是设 \(dp_{u,k}\) 为子树内深度取 \(k\) 的答案。

在 dp 时采用 dfs 形式,设当前是 \(dfs(u,tp)\),我们遵循以下步骤:

  • \(u=tp\),开一个 vector 长度为 \(mxd_u-dep_u+1\) 存储每个深度 \(-dep_{tp}\) 的 dp 值。
  • 优先递归长儿子
  • 递归非长儿子,并暴力合并信息(枚举长儿子第二维 \(j\) 并找到在 \(dp_{tp}\) 的相应位置进行更新)
  • 加入点 \(u\) 的信息
  • 统计答案

注意到存储的时候有个 \(dep_{tp}\) 的下标偏移。

模板:求各个点子树内各个节点深度的众数。

void dfs2(int u,int tp){
    if(u==tp)f[u].resize(mxd[u]-dep[u]+1);
    if(son[u])dfs2(son[u],tp);
    for(auto v:e[u])if(v!=lt[u]&&v!=son[u]){
        dfs2(v,v);
        for(int i=0;i<f[v].size();++i){
            f[tp][i-dep[tp]+dep[v]]+=f[v][i];
            if(f[tp][i-dep[tp]+dep[v]]>mx[tp]||(f[tp][i-dep[tp]+dep[v]]==mx[tp]&&i-dep[tp]+dep[v]<id[tp]))mx[tp]=f[tp][i-dep[tp]+dep[v]],id[tp]=i-dep[tp]+dep[v];
        }
    }
    f[tp][dep[u]-dep[tp]]++;
    if(f[tp][dep[u]-dep[tp]]>mx[tp]||f[tp][dep[u]-dep[tp]]==mx[tp]&&dep[u]-dep[tp]<id[tp])mx[tp]=f[tp][dep[u]-dep[tp]],id[tp]=dep[u]-dep[tp];
    ans[u]=id[tp]-dep[u]+dep[tp];
    // cout<<"dfs2 "<<u<<' '<<mx[tp]<<" "<<id[tp]<<"\n";
}
posted @ 2024-10-21 17:04  spdarkle  阅读(5)  评论(0编辑  收藏  举报