对于LCA的一些理解
RMQ
dfs处理树
对于一个树形结构,可以用dfs将一颗树转化成数组,数组中记录每个点的标号,这样数组就按照dfs的顺序把树存了下来确定祖先的范围
对于询问的节点X和Y, X、Y的祖先一定存在于数组中X、Y第一次出现的区间内,而且祖先就是区间内深度最小的节点RMQ
dfs的复杂度就是树边数的二倍,所以区间查询需要优化,这里可以用RMQ算法,预处理结果O(1)得到最值
void dfs(int pre, int x, int step) {
depth[x] = step;
first[x] = cnt;
tree[cnt] = x;
cnt++;
int len = g[x].size();
for (int i = 0; i < len; ++i) {
if (g[x][i] == pre) continue;
dfs(x, g[x][i], step + 1);
tree[cnt++] = x;
}
return;
}
int Min(int a, int b) {
if (depth[a] < depth[b]) return a;
else return b;
}
void RMQ() {
for (int i = 1; i <= cnt; ++i) {
dp[i][0] = tree[i];
}
for (int i = 1; (1 << i) <= cnt; ++i) {
for (int j = 1; j + (1 << i) - 1 <= cnt; ++j) {
dp[j][i] = Min(dp[j][i - 1], dp[j + (1 << (i - 1))][i - 1]);
}
}
}
int LCA(int a, int b) {
int l = first[a];
int r = first[b];
if (l > r) swap(l, r);
int len = log2(r - l + 1);
return Min(dp[l][len], dp[r - (1 << len) + 1][len]);
}
树上倍增
father[ i ] [ j ]表示 i 的第 2 ^ j 个父亲
- dfs
dfs在处理深度的同时更新每个人的father数组
void dfs(int pre, int x, int step) {
// 更新father
for (int i = 1; i < log2(n); ++i) {
if (fa[x][i - 1] == 0) break;
fa[x][i] = fa[fa[x][i - 1]][i - 1];
}
// 记录深度
depth[x] = step;
int len = g[x].size();
for (int i = 0; i < len; ++i) {
if (g[x][i] == pre) continue;
fa[g[x][i]][0] = x;
dfs(x, g[x][i], step + 1);
}
}
- LCA
首先将两个节点的深度调成一样,如果深度相同而且是同一个点就找到最近的祖先了,如果深度相同但是节点不同,这时就要向上倍增,需要注意的是倍增的中止条件不是两个节点相同,而是节点的父亲相同。因为每次倍增的范围很大,很可能超过最近公共祖先,我们可以从最大的祖先开始,如果祖先相同缩小范围,如果祖先不同更新两个点的状态继续找,最后两个人的最近公共祖先一定是father[ i ][ 0 ]
int LCA(int a, int b) {
if (depth[a] < depth[b]) swap(a, b);
int depth_x = depth[a] - depth[b];
// 先将两个点的深度调成一样
for (int i = 0; i <= log2(depth_x); ++i) {
if (depth_x & (1 << i))
a = fa[a][i];
}
// 如果深度一样,而且相同直接返回
if (a == b) return a;
// 从最远的父亲开始
for (int i = log2(depth[a]); i >= 0; --i) {
// 如果父亲不同就向上更新
if (fa[a][i] != fa[b][i]) {
a = fa[a][i];
b = fa[b][i];
}
}
return fa[a][0];
}
求树上两点的距离
树上两点的距离是唯一的,距离 = depth[X] + depth[Y] - 2 * depth[ancestor]
线段重合的长度
思想和求距离一样,例如A到C和B到C的重合长度 = (dis_ac + dis_bc - dis_bc)/ 2
int dis(int a, int b) {
int ancestor = LCA(a, b);
int depth_a = depth[a];
int depth_b = depth[b];
int depth_ancestor = depth[ancestor];
return depth_a + depth_b - 2 * depth_ancestor;
}