关于LCA的几个想法

  

昨天在zxz的提示下我去补了LCA(最近公共祖先),有感而作

1.学习

在我学习LCA的时候,遇到不会的,我直接选择的了逃避……直接学 习其他博主写的code。导致我到最好也没有学到什么东西…… 所幸,我始终有很好的同学的来指出的我的错误 (在这里悄悄感谢他们) 于是有了2

2.思考

正文开始: 我们在LCA函数中是要让两个孩子不不断上升的,最后找到他们的 (爹) 祖先, 于是乎,怎么来迫近这个祖先呢: 有如下想法 ### 1.朴素 首先我们是在一个多叉树上找找祖先的,那么有个性质节点的父节点的 ==深度(depth)== 为该节点 ==-1== (应该) 那么我们可以把不在同一层的孩子上升的到节点(根据深度差),然后依次同步向上,直到重合, ==第一个重合的点== 自然就是最近公共祖先啦 (以上做法毕竟朴素,容易TLE (寄掉) ### 2倍增 (又到了人见人爱(詪)的倍增了…… 用倍增,一半就不会TLE了,在预处理(把孩子上升到同一高度上)和不断上升进行了优化, talk is cheap,show me your code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
 int LCA(int al, int bl) {	

//there: make depth[al] < depth[bl]
//=== al higher than bl
if (depth[al] > depth[bl]) {
swap(al, bl);
}
int x_h = depth[bl] - depth[al];
for (int i = 0; i <= 20; i++) {
if (x_h & (1 << i)) {
//用(1<<i)的原因是f[][i]这个第二的数组条件是二进制表示的
bl = father[bl][i];

}
}
if (al == bl) {return al;}
for (int i = 19; i >= 0; i--) {
if (father[al][i] != father[bl][i]) {
//只要是公共祖先那么就向下
//直到不是公共祖先,那么该点的父亲就是最近公共祖先
//逆向思维,妙啊§(* ̄▽ ̄*)§
al = father[al][i];
bl = father[bl][i];
}
}
return father[bl][0];
//朴素:
// for (int i = 1; i <= x_h; i++) {
// bl = father[bl][0];
// }
// for (int i = 1; i < 20; i++) {
// if (al == bl) {return al;}
// al = father[al][0];
// bl = father[bl][0];
// }

}
倍增的基本思想在这里就不再赘述(关键我讲的也不好 主要就是利用了最大的二进制(不超过界限),不断去逼近(就是,只要不超过就跳最大的,省时省力)

前方核聚变打击警告!!!__ :下面就昨天晚上睡不着觉想的…… ### 3. 不怕犯错 *** No.1

新建vis[]判断是否访问过,两个last_place 记录上一次节点。 step 1. 先用倍增不断向上,直到访问过或者到顶,(按理说,到了根节点也是最坏情况,也算一种访问过) *** No.2

step 2. 然后向下倍增(从小开始),知道两个点分别向下的后的节点不同,再返回上一个相同点。 从上一个相同点重新开始向下倍增,知道2^0的点不相同,那么当前节点就是最近公共祖先; step 3. 然后就是在边界上处理一下,(感觉也没有快多少,反而更难实现了……QwQ

没关系,不怕犯错! ### 小聪明 把两个孩子到根节点的路径遍历一边,将记录到一个数组中国, 然后sort一下(应该需要把……) 然后进行伪去重(应该是unique(); 然后直接找最后一个就行嘿嘿……

补:好像 unique(); ,不是很行……,手写吧……


🍂全文完↩︎