LCA随堂笔记

P3379 【模板】最近公共祖先(LCA)为例题

1.公共祖先:

一棵树上,有父节点和子节点,那么如何才能知道两个节点的共同祖先是谁?
拿左图为例,假设4是整个树的祖先,那么(5,3)的公共祖先就有1和4

2.最近公共祖先LCA:

显然(5,3)有两个公共祖先1和4,那么请问如何才能求出距离(5,3)最近的公共祖先1?

3.设dep[x]表示x在树上的深度

那么当dep[x] > dep[y]时,说明x更深,需要往上跳
当dep[x] == dep[y]时,说明跳到的层级是一样的
如果层级一样且x == y,那么说明此时的x就是之前查询两点最近公共祖先LCA

4.弊端,

假设有m=10W查询,n=10W节点,那么最坏情况下,每次都从最低端往上跳,跳到最顶端那就是要跳n次,总时间复杂度为:O(n * m)超时,合理应该是n或m变成log级别才能通过

考虑到m次查询不能减少,所以目标就转变为将n次跳跃变成logn次
即:每次不能往上跳一级,而是要跳倍数级

5.任何一个数字都可以被拆分成若干个2的次方相加得来

例如9 = 8 + 1 = 2^3 + 2^0
10 = 8 + 2 = 2^3 + 2^1
所以在诸多倍数中,选择跳2的倍数步可以保证覆盖所有的节点
则时间复杂度降低为m * log2n

倍增求LCA

6.建树,使用dfs(int x,int fa) fa表示x的父节点

主函数调用dfs(1,0),设1为根结点开始建树,默认节点1的父节点为0dep[x]:x在树上的深度
dep[x] = dep[fa] + 1

假设y是和x链接的节点,如果y != fa,说明y可以是x的子节点
那么dfs(y,x)
例如当x = 1,y = 5时,就会触发dfs(5,1)
例如当x = 1,y = 3时,就会触发dfs(3,1)

dfs结束后,得到dep数组,根据查询节点的dep数组的大小,决定节点是否要继续往上跳

 

7.往上跳,怎么知道跳哪了?

往上跳1步,到哪?
往上跳2步,到哪?
往上跳4步,到哪?
。。。
往上跳1024步,到哪?
往上跳2的多少次方,才算够?

往上跳最多2^20,包够

 

8.往上跳的状态

设f[x][i]:在x点往上跳2^i次方步到哪

因为

2^i = 2^(i-1) * 2

2^i = 2^(i-1) + 2^(i-1)

所以往上跳2^i步,可以转换为:
先往上跳2^(i-1)步到达中间节点y
再从y往上跳2^(i-1)步到达目标点

 


先设y = f[x][i - 1],x只往上跳2^(i - 1)步
然后再让y往上跳剩下的2^(i - 1)步
即:
int y = f[x][i - 1]
f[x][i] = f[y][i - 1]

1 2 3 4 5 6 7 8 9 10 11 12

f[1][3] 从1往上跳8步到9

f[1][2] 先从1往上跳4步到5
再从f[5][2]从5往上跳4步到9


9.以上,全部发生在dfs预处理阶段


可以得到整棵树的dep[N]深度数组
和f[x][i]:x往上跳2^i步能到哪
目前还没求最近公共祖先


10.lca(x,y)返回x和y的最近公共祖先函数

我们之前说过让x和y深度较深的那一个先往上跳
跳到dep[x] == dep[y]深度相同,然后一起往上跳
只不过之前是一步步往上,现在是一次跳2^i步

但是往上跳也有说法,i从小到大跳,还是i从大到小跳?

从贪心的角度出发,i从小到大跟一步步跳没区别,所以最快到达祖先的方式是从大到小跳


11.两点距离公式


dep[x] + dep[y] - 2 * dep[lca(x,y)]
dep[x]:从根结点到达x的距离

P4281:求三点的LCA计算到达LCA的距离之和

 

12.树上差分P3128


设差分数组d[i]:第i个数字和前一个数字的差值
d[i] = a[i] - a[i - 1]
对于一个区间修改[3,4]增加2
d[3] += 2, d[4 + 1] -= 2
d[L] += 2,d[R + 1] -= 2

a = 3 1 6 5 4
d = 3 -2 5 -1 -1
区间修改[3,4]增加2
a = 3 1 8 7 4

d = 3 -2 7 -1 -3


13.对于树上让[x,y]路径都 + 1


设lca(x,y) == rt
转变为x -> rt 和y -> rt
转变[x,rt] + 1和[y,rt] + 1
再设frt表示rt的父节点,再根据差分公式,则有
d[x]++,d[frt]--
d[y]++,d[frt]--
注意:
1.frt被减了两次,实际只需要被减1次
2.因为rt被包含在了两个区间[x,rt]和[y,rt] ,所以d[rt]被加了两次
即:d[rt]--

总结:对于路径[x,y] + 1来说
int rt = lca(x,y),frt = f[rt][0];
d[x]++,d[y]++,d[rt]--,d[frt]--;


14.树上差分,如何复原?


树上差分,通过再来一次dfs遍历所有的节点
使得父节点x,可以将所有子节点y的d[y]累加起来实现复原的效果

 

posted @ 2025-05-10 21:49  CRt0729  阅读(12)  评论(0)    收藏  举报