长链剖分 入门
长链剖分
额,其实和树剖差不多,对于每个节点
那么令
同样令连接不同长链的两个点之间的边为虚边。
有如下性质:
- 从根到节点
,所经过虚边个数不超过 ,这是因为从 开始往上走,所走到的每一条长链其长度都是严格长于当前长链的,即使 相同,往上走的那条链至少还会加上父亲,这告诉我们走到根,所有的长链最多也就是 - 每条链底是叶子,且是子树内最深节点
- 节点
的 级祖先所在长链长度不小于 。
树上 级祖先
我们先利用倍增预处理出
查询
这样就满足
我们对于每个链,维护从链顶往上走
在存储时,我们可以类似树剖进行
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。
例如子树内深度为
一般形式是设
在 dp 时采用 dfs 形式,设当前是
- 若
,开一个vector
长度为 存储每个深度 的 dp 值。 - 优先递归长儿子
- 递归非长儿子,并暴力合并信息(枚举长儿子第二维
并找到在 的相应位置进行更新) - 加入点
的信息 - 统计答案
注意到存储的时候有个
模板:求各个点子树内各个节点深度的众数。
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";
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!