#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)); } }
非优美注释版
----------------------------------------------------------
#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数组需要开两倍于边数
原文地址:https://www.cnblogs.com/dudujerry/p/10192526.html
时间: 2024-10-10 02:00:33