【树剖求LCA】树剖知识点
不太优美但是有注释的版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | #include<cstdio> #include<iostream> using namespace std; struct edge{ int to,ne; }e[1000005]; int n,m,s,ecnt,head[500005],dep[500005],siz[500005],son[500005],top[500005],f[500005]; void add( int x, int y) //加边 { e[++ecnt].to=y; e[ecnt].ne=head[x]; head[x]=ecnt; } void dfs1( int x) //构造树 { siz[x]=1; //假设当前节点仅有一个儿子 dep[x]=dep[f[x]]+1; //当前节点深度=父亲节点深度+1 for ( int i=head[x];i;i=e[i].ne) //遍历所有的子节点 { int dd=e[i].to; if (dd==f[x]) continue ; //如果是父节点,则略过 f[dd]=x; //那么确定x是当前节点的父亲 dfs1(dd); //向下遍历 siz[x]+=siz[dd]; //遍历完子树之后,加上子树的大小 if (!son[x]||siz[son[x]]<siz[dd]) //如果x节点重儿子未确定或者重儿子的子树比当前遍历节点的子树小 son[x]=dd; //更新重儿子 } } void dfs2( int x, int tv) //求重链 { top[x]=tv; //设置x所在重链顶为tv if (son[x])dfs2(son[x],tv); //如果x有重儿子,那么随着这条重链走 for ( int i=head[x];i;i=e[i].ne) { int dd=e[i].to; if (dd==f[x]||dd==son[x]) continue ; //如果走到父亲或者走到重儿子(已经走过重儿子,避免重复),那么跳过 dfs2(dd,dd); //开启一条新链,链顶是其本身 } } int lca( int x, int y) { while (top[x]!=top[y]) //如果二者不在同一条重链上 { if (dep[top[x]] >= dep[top[y]]) x=f[top[x]]; //选择所在重链的顶的深度较大的点向上跳,目的是防止跳过LCA else y=f[top[y]]; } return dep[x] < dep[y] ?x :y; //当二者在同一条重链上的时候,选择深度较浅的点即为lca } int main() { scanf ( "%d%d%d" ,&n,&m,&s); for ( int i=1;i<n;++i) { int x,y; scanf ( "%d%d" ,&x,&y); add(x,y); add(y,x); } dfs1(s); dfs2(s,s); for ( int i=1;i<=m;++i) { int x,y; scanf ( "%d%d" ,&x,&y); printf ( "%d\n" ,lca(x,y)); } } |
比较优美但是没注释的版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | #include<iostream> #include<vector> #include<cstdio> #include<queue> #include<map> #include<cstdlib> #include<cmath> #include<algorithm> #include<set> #include<cstring> using namespace std; typedef long long ll; const ll INF=99999999; const int N = 500010; int n,m,s; struct edge{ int to,ne; }e[N*2]; int top[N],siz[N],son[N],fa[N],dep[N]; int head[N],ecnt = 1; void add( int x, int y) { e[ecnt].to = y; e[ecnt].ne = head[x]; head[x] = ecnt++; } void dfs1( int x) { siz[x] = 1; dep[x] = dep[fa[x]] + 1; for ( int i = head[x];i;i = e[i].ne){ int t = e[i].to; if (t == fa[x]) continue ; fa[t] = x; dfs1(t); siz[x] += siz[t]; if (!son[x]||siz[son[x]] < siz[t]) son[x] = t; } } void dfs2( int x, int tp) { top[x] = tp; if (son[x]) dfs2(son[x],tp); for ( int i = head[x];i;i = e[i].ne){ int t = e[i].to; if (t == son[x]||t == fa[x]) continue ; dfs2(t,t); } } int lca( int x, int y) { while (top[x] != top[y]){ if (dep[top[x]] >= dep[top[y]]) x = fa[top[x]]; else y = fa[top[y]]; } return dep[x] < dep[y] ?x :y; } int main() { scanf ( "%d%d%d" ,&n,&m,&s); for ( int i = 1;i < n;i++){ int x,y; scanf ( "%d%d" ,&x,&y); add(x,y); add(y,x); } dfs1(s); dfs2(s,s); for ( int i = 1;i <= m;i++){ int a,b; scanf ( "%d%d" ,&a,&b); printf ( "%d\n" ,lca(a,b)); } return 0; } |
树剖理解容易,需要注意的是题目如果给的是双向边,e数组需要开两倍于边数
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!