voiddfs(int cur, int fath)// 传入当前节点和父节点 { if (vis[cur]) // 如果已经走过,就返回 return ; vis[cur] = true; // 标记为走过 dep[cur] = dep[fath] + 1; // 每个点的深度为它的父节点的深度 + 1 fa[cur][0] = fath; // 那么 2 的 0 次就是 1 级祖先,也就是向上一层的祖先,是它的父亲 for (int i = 1; i <= Log2[dep[cur]]; i ++) // 使用了常数优化,处理出祖先 fa[cur][i] = fa[fa[cur][i - 1]][i - 1]; // 以翻一番的速度增长,也就是这个点的父亲的父亲。 for (int i = head[cur]; i; i = edge[i].nxt) // 前向星遍历 dfs (edge[i].to, cur); // 向下递归 } inlineintLCA(int a, int b)// 最近公共祖先 { if (dep[a] > dep[b]) swap (a, b); // 保证 b 的深度大于 a while (dep[a] != dep[b]) // 先使其在同一深度 b = fa[b][Log2[dep[b] - dep[a]]]; // 每次算一边深度差距, b 指针通过它的父节点逐渐到达 a 的深度 if (a == b) // 如果这时两者已在同一个点 return a; // 说明 a 就是一个父节点(同样是 b 的父节点) for (int k = Log2[dep[a]]; k >= 0; k --) // 遍历 2 的 k 次结构的数( k 就是级数) if (fa[a][k] != fa[b][k]) // 如果它们的父节点仍不相同 a = fa[a][k], b = fa[b][k]; // 向上移动指针 return fa[a][0]; // 最后返回这个指针的父亲(因为上面在判断到相同时直接退出了) }
当然也有别的简单的写法(给出小鱼的代码)。
在 里可以这么写:
1 2 3 4 5 6 7 8 9
for (int i = 1; (1 << i) <= dep[cur]; i ++) fa[cur][i] = fa[fa[cur][i - 1]][i - 1]; for (int i = head[cur]; i; i = edge[i].nxt) { int v = edge[i].to; if (v == fath) continue; dfs (v, cur); }
在 里保持相同高度时可以这么写:
1 2 3
for (int i = 20; i >= 0; i --) if (dep[fa[b][i]] >= dep[a]) b = fa[b][i];