LCA——树链剖分
这里是我学习LCA的最后一种方法——树链剖分。
那么首先就先讲一下树链剖分是什么:
补充一下一些概念,下面会用:
重儿子:一个节点的子节点中作为根节点的子树中节点最多的子节点。
轻儿子:一个节点的子节点中除重儿子的子节点。
链:起点为轻儿子或根节点,其余为重儿子的链。
树链剖分:顾名思义,将树剖成一条一条的链,要将树剖成链需要两个dfs。
1.搜索求出每个节点的重儿子和父节点。
2.根据节点的重儿子搜索出每一条链,记录节点的所在链和编号(链的起点越靠近根节点的编号越小,同一条链上越靠近起点的编号越小)。
下面求LCA:
若两个节点不在一条链上,将编号更大的那个更新(更新成它所在链的起点的父节点),直到他们在同一个链上时,编号更小的就是他们的最近公共祖先。
代码如下:
#include<iostream> using namespace std; int read(){ int x=0,f=1; char a=getchar(); while(a<'0'||a>'9'){ if(a=='-')f=-1; a=getchar(); } while(a>='0'&&a<='9'){ x*=10; x+=a-'0'; a=getchar(); } return x*f; } int n,m,s; int head[500010],tot; int vis[500010],f[500010],sum[500010],zs[500010]; int dui[500010],id[500010],cnt; struct node{ int to; int nxt; }edge[1000010]; void build(int u,int v){ edge[++tot].to=v; edge[tot].nxt=head[u]; head[u]=tot; } void dfs1(int u){ sum[u]=1; for(int i=head[u];i;i=edge[i].nxt){ int v=edge[i].to; if(vis[v]==0){ vis[v]=1; f[v]=u; dfs1(v); sum[u]+=sum[v]; if(sum[v]>sum[zs[u]]){ zs[u]=v; } } } } void dfs2(int u,int t){ dui[u]=t; id[u]=++cnt; if(!zs[u])return ; dfs2(zs[u],t); for(int i=head[u];i;i=edge[i].nxt){ int v=edge[i].to; if(v!=f[u]&&v!=zs[u]){ dfs2(v,v); } } } int main(){ n=read();m=read();s=read(); for(int i=1;i<n;i++){ int u,v; u=read();v=read(); build(u,v); build(v,u); } vis[s]=1; f[s]=s; dfs1(s); dfs2(s,s); for(int i=1;i<=m;i++){ int u,v; u=read();v=read(); while(dui[u]!=dui[v]){ if(id[u]>id[v]){ u=dui[u]; u=f[u]; } else{ v=dui[v]; v=f[v]; } } if(id[u]<id[v]){ printf("%d\n",u); } else{ printf("%d\n",v); } } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现