长链剖分
额,其实和树剖差不多,对于每个节点 \(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";
}
标签:长链,入门,剖分,dep,tp,son,int,mxd
From: https://www.cnblogs.com/spdarkle/p/18489838