LCA_Tarjan
参考博客:https://www.cnblogs.com/JVxie/p/4854719.html
LCA的Tarjan写法需要结合并查集
从叶子节点往上并
int Find (int x) { return x == pre[x] ? x:pre[x] = Find(pre[x]); } void dfs(int x,int w,int fa) { d[x] = w; //点x的深度 //遍历与点x相连的点(除了已经访问过的和其父节点) for (int i = 0; i < v[x].size(); i++) { if (!vis[v[x][i]] && v[x][i] != fa) { dfs(v[x][i],w+1,x); pre[v[x][i]] = x; vis[v[x][i]] = 1; } } //访问所有和x有询问关系的e //如果e被访问过; //x,e的最近公共祖先为find(e); }
LCA_倍增
参考博客:https://www.cnblogs.com/zhouzhendong/p/7256007.html
先写暴力写法:当x,y深度不同时先把深的调到和浅的同一深度再一起往前面跳找其最近公共祖先。
void dfs(int f,int u){ fa[u]=f; d[u]=d[f]+1; for (int i=0;i<v[u].size();i++) if (v[u][i]!=f) dfs(u,v[u][i]); } int LCA(int a,int b){ if (d[a]>d[b]) swap(a,b); while (d[b]>d[a]) b=fa[b]; while (a!=b) a=fa[a],b=fa[b]; return a; }
显然,一个一个的跳太慢了,我们可以考虑通过二进制进行优化,也就是倍增。
void dfs(int f,int u) { fa[u][0] = f; dep[u] = dep[f] + 1; for (int i = 1; i <= 15; i++) { fa[u][i] = fa[fa[u][i-1]][i-1]; //其他操作 } for (int i = 0; i < v[u].size(); i++) { if (f == v[u][i]) continue; dfs(u,v[u][i]); } } void lca(int x,int y) { memset(ans,0,sizeof(ans)); if (dep[x] < dep[y]) swap(x,y); for (int i = 15; i >= 0; i--) if (dep[fa[x][i]] >= dep[y]) { //其他操作 x = fa[x][i]; } if (x == y) { return; } for (int i = 15; i >= 0; i--) if (fa[x][i] != fa[y][i]) { //其他操作 x = fa[x][i],y = fa[y][i]; } }
例题:洛谷P3292 [SCOI2016]幸运数字 | 题解