LCA离线算法学习笔记
2017-04-11 15:11
246 查看
算法思想:
LCA离线,采用的是递归的tarjan算法,利用了树深度优先遍历的性质可以在一次遍历过程中巧妙的求解出查询的最接近公共祖先,时间复杂度是O(n+q),但前提是需要离线保存所有询问。算法思路如下:
当前dfs遍历到节点u,先将vis[u]标记成true,然后处理以u为根节点的子树,子树处理完成后,将这棵子树中的所有点的祖先都设成u,这时候处理跟u相关的所有询问(u,v),如果vis[v]==true,那么lca(u,v)就等于v当前设置的祖先。
举个例子,示意图如下:
对于正确性,这里对于询问v考虑三种情况:
v属于u的子树,这时v已经被访问过,所以vis[v]==true。由于之前处理的了u的子节点的祖先都为u,所以v当前设置的祖先也是u,故lca(u,v)=u,很显然是正确的。
v属于u的祖先,既然是dfs过程,在访问到u之前,u的祖先一定已经访问过,所以vis[v]==true。因为当前处理的是v的子树u,所以以v为根的子树还没有被处理完全,故这时候v的祖先还是它自己,所以lca(u,v)=v,正确。
v属于u的兄弟节点或者u祖先的兄弟节点为根的子树中,这样v有可能被访问过,也有可能没有。没有访问过自然不需要处理,如果v被访问了,那么v当前设置的祖先是什么呢?以上图作为例子,假设u是5,v是3,在访问到u的时候,v已经被访问了,那么根据算法的步骤,此时v的祖先被设置成节点1,从图中观察,lca(u,v)确实就是节点1,故算法正确。
其实tarjan算法就是利用了树的dfs的性质,从一个节点v遍历到另一个节点u,肯定会经过lca(u,v),此时按照算法将v的祖先就设置成lca(u,v),就可以保证算法正确。至于寻找祖先的操作,很显然就利用并查集来优化查询速度。
例题:
还是用LCA入门题:HDU-2586题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=2586
#include <bits/stdc++.h> using namespace std; const int MAXN = 44444; struct Node { int v, w; }; struct QNode { int v, id; }; struct Query { int u, v, lca; }; struct LCA { int n, m; vector <Node> tree[MAXN]; vector <QNode> qtree[MAXN]; Query q[MAXN]; int dist[MAXN], pa[MAXN]; bool vis[MAXN]; void init(int n, int m) { this->n = n; this->m = m; memset(vis, false, sizeof(vis)); memset(dist, 0, sizeof(dist)); for (int i = 1; i <= n; i++) pa[i] = i; for (int i = 1; i <= n; i++) tree[i].clear(); for (int i = 1; i <= m; i++) qtree[i].clear(); } void addEdge(int u, int v, int w) { tree[u].push_back((Node) {v, w}); tree[v].push_back((Node) {u, w}); } void addQuEdge(int u, int v, int id) { q[id] = (Query){u, v, -1}; qtree[u].push_back((QNode) {v, id}); qtree[v].push_back((QNode) {u, id}); } int Find(int x) { return pa[x] == x ? x : pa[x] = Find(pa[x]); } void tarjan(int u) { vis[u] = true; for (int i = 0; i < (int)tree[u].size(); i++) { int v = tree[u][i].v, w = tree[u][i].w; if (vis[v]) continue; dist[v] = dist[u] + w; tarjan(v); pa[v] = u; } for (int i = 0; i < (int)qtree[u].size(); i++) { int v = qtree[u][i].v, id = qtree[u][i].id; if (vis[v]) { q[id].lca = Find(v); } } } } Lca; int main() { //freopen("in.txt", "r", stdin); int T; scanf("%d", &T); while (T--) { int n, m; scanf("%d%d", &n, &m); Lca.init(n, m); for (int i = 1; i < n; i++) { int u, v, w; scanf("%d%d%d", &u, &v, &w); Lca.addEdge(u, v, w); } for (int i = 1; i <= m; i++) { int u, v; scanf("%d%d", &u, &v); Lca.addQuEdge(u, v, i); } Lca.tarjan(1); for (int i = 1; i <= m; i++) { int u = Lca.q[i].u, v = Lca.q[i].v, lca = Lca.q[i].lca; printf("%d\n", Lca.dist[u] + Lca.dist[v] - 2 * Lca.dist[lca]); } } return 0; }
相关文章推荐
- [笔记]LCA 最近公共祖先---tarjan离线算法
- lca Tarjan学习笔记
- LCA tarjan 离线算法学习
- 倍增lca学习笔记(codevs2370小机房的树题解)
- LCA学习笔记
- Tarjan 学习笔记 - LCA
- 学习笔记:tarjan求lca
- LCA三种算法学习(离线算法tarjan+在线算法转rmq+在线倍增)例题poj1330、1470;hdu4547、2874
- LCA在线算法学习笔记
- 【算法笔记】LCA问题-tarjan 离线算法
- |算法讨论|LCA 学习笔记
- Win32学习笔记 第二章 Unicode
- PE学习笔记(一)
- Win32学习笔记 第三章 HelloWin
- 开发asp.net自定义控件(asp.net学习笔记五)
- Win32学习笔记 第四章 输出文本_2
- Microsoft Agent 学习笔记 (一)
- STL的学习笔记之一
- 《Mastering Delphi 6》学习笔记之七
- 开发asp.net自定义控件(asp.net学习笔记四)