文章目录
- 相关链接
- 模板题目
-
- [1483. 树节点的第 K 个祖先](#1483. 树节点的第 K 个祖先)
- 最近公共祖先LCA的求法
- 练习题目
-
- [2836. 在传球游戏中最大化函数值](#2836. 在传球游戏中最大化函数值)
- [2846. 边权重均等查询](#2846. 边权重均等查询)
- 扩展题目
相关链接
把相关链接放在最前面是因为,周赛遇到了几次,不会做。这才想起来学一下这个算法。
【力扣周赛】第 360 场周赛(⭐树上倍增)
【力扣周赛】第 361 场周赛(⭐树上倍增、LCA)
模板题目
1483. 树节点的第 K 个祖先
https://leetcode.cn/problems/kth-ancestor-of-a-tree-node/description/
提示:
1 <= k <= n <= 5 * 10^4
parent[0] == -1 表示编号为 0 的节点是根节点。
对于所有的 0 < i < n ,0 <= parent[i] < n 总成立
0 <= node < n
至多查询 5 * 10^4 次
java
class TreeAncestor {
int[][] pa;
// 使用原始数据将整个 pa 数组预处理出来
public TreeAncestor(int n, int[] parent) {
int m = 32 - Integer.numberOfLeadingZeros(n); // n的二进制长度
pa = new int[n][m]; // 表示节点i的2^j个祖宗节点
// 初始化dp数组,即填充每个节点的父亲节点
for (int i = 0; i < n; ++i) {
pa[i][0] = parent[i];
}
// 先枚举i,再枚举x
// 相当于先算出所有爷爷节点,再算出所有爷爷的爷爷节点
for (int i = 0; i < m - 1; i++) {
for (int x = 0; x < n; ++x) {
int p = pa[x][i]; // 取出x的第2^i个祖宗节点
// x的第2^(i+1)个祖宗节点 等于 x的第2^i个祖宗节点的第2^i个祖宗节点
pa[x][i + 1] = p < 0? -1: pa[p][i];
}
}
}
// 取出node节点的第k个祖宗节点
public int getKthAncestor(int node, int k) {
// 写法1 从低位到高位枚举
// int m = 32 - Integer.numberOfLeadingZeros(k); // k的二进制长度
// for (int i = 0; i < m; ++i) {
// if ((k >> i & 1) == 1) { // k的二进制当前位为1
// node = pa[node][i];
// if (node < 0) break;
// }
// }
// return node;
// 写法2 不断去掉k末尾的1
for (; k != 0 && node != -1; k &= k - 1) {
node = pa[node][Integer.numberOfTrailingZeros(k)];
}
return node;
}
}
/**
* Your TreeAncestor object will be instantiated and called as such:
* TreeAncestor obj = new TreeAncestor(n, parent);
* int param_1 = obj.getKthAncestor(node,k);
*/
最近公共祖先LCA的求法
两个节点 x 和 y,
先将 x 和 y 放在同一个深度上,即先让 y 跳 depth[y] - depth[x]。
当 x 和 y 在同一深度之后,如果 x = y,那么 x 就是 lca,否则 把 x 和 y 一起往上跳。
由于不知道 lca 的具体位置,因此从大到小枚举 i,尝试是否可以向上跳,直到 pa[x][i] == pa[y][i],循环结束之后,有 lca = pa[x][0]。
练习题目
2836. 在传球游戏中最大化函数值
https://leetcode.cn/problems/maximize-value-of-function-in-a-ball-passing-game/
2846. 边权重均等查询
https://leetcode.cn/problems/minimum-edge-weight-equilibrium-queries-in-a-tree/description/
扩展题目
把练习题目做明白就完了,咱先不扩展。