大纲
1.树上倍增&欧拉序+RMQ
2.Tarjan
3.例题
1.树上倍&欧拉序+RMQ
定义:给定一棵有根树,若节点u既是节点x的祖先,也是节点y的祖先,则称u是x和y的公共祖先。
在x,y的所有公共祖先中,深度最大的一个称为x,y的最近公共祖先,记为LCA(x, y)。
例如:下图中,2和3的最近公共祖先是1, 2和5的公共祖先是2。4和9的最近公共祖先是2。
两个结点的公共祖先可能有多个(例如2和1都是4,5的公共祖先),但最近公共祖先只有一个。
向上标记:
① 从x向上遍历,直到根节点,遍历过程中标记所有x的祖先。
② 从y向上遍历,遍历过程中首次遇到已标记结点时,该节点就是LCA(x, y);
向上标记法单次询问时间复杂度为:O(N)。
朴素算法:
① DFS/BFS求出树中每个节点的深度。
② 将x和y中深度较大的向上调整,直到x和y的深度相同。
③ 将x和y同时向上调整,直到相遇,首次相遇的结点即是x和y的最近公共祖先。
朴素算法单次询问时间复杂度为:O(N)。
在朴素算法中x和y向上调整时都是一步一步向上调整,倍增法使用倍增的方式向上调整。
在将x和y以倍增的方式向上调整时,如何知道倍增后的祖先是谁?
定义p[i][j]表示节点i的2^j祖先,倍增过程中通过p数组直接获取倍增后的祖先。
如何预处理p[i][j]的值?
i的2 ^ j祖先是i的2 ^ (j-1)祖先的2(j-1)祖先,即:p[i][j] = p[p[i][j-1]][j-1];
可以在dfs/bfs计算结点深度的同时初始化p数组,因为无论是dfs还是bfs都是
从根节点出发,当搜索到节点x时,其所有祖先一定都搜索过了。
预处理p数组时间复杂度为:O(Nlog(N))。
① DFS/BFS求出树中每个节点的深度,并预处理除任意节点的2 ^ j祖先,即: p数组。
② 将x和y中深度较大的以倍增的方式向上调整,直到x和y的深度相同。
if (d[x] < d[y])
swap (x , y) ;
for (int j = log2n; j >= 0; j --)
if (d