今天学习了一发LCA算法
所谓LCA,就是最近公共祖先,lowest common ancestor
有O(n)的朴素算法,详见挑战程序设计竞赛 328页
LCA一般有两种解法,都是O(logn)复杂度的
1.基于二分搜索的算法
2.基于RMQ的算法
这些都是书上有的
所以用一个题目来解释把
POJ 2763
这题说实话还是有点难度的,因为找LCA的时候还要计算边权和,用二分搜索(倍增法)的时候可以记录边权和,但是还要修改边权,这样的话倍增法就非常难以快速修改
所以这题要用基于RMQ的LCA算法,首先RMQ算法的编号数量是节点个数*2-1,BIT的节点个数也是这些,所以就可以用一个时间戳k搞定
首先你要求a-b的距离,那么就是dis[a]+dis[b]-2*dis[lca(a,b)]
dis是这个节点到root的距离,这个公式是很好理解的
求lca用RMQ的方法,只要求id[a]和id[b]之间depth最小的编号i就行,然后vs[i]就是节点
主要这个修改是亮点,区间修改一般都是用线段树来维护的,但是这题线段树太复杂了,反正我不会
听说树链剖分可以解决这种问题,我也不懂,于是就看题解用的BIT(树状数组)
因为RMQ把每个节点走的顺序都列出来了,所以如果修改的是edge[a][b],fa[b]==a的话,只要修改b以及b为根下面的子树到根节点的距离,所以要开个边数组记录每条边的端点和权值,然后做修改
b以及b以下的节点,在RMQ的那个列表中,就是第一个b出现的编号和最后一个b出现的编号之间的那个区间,并且树状数组i不能从0开始,所以编号要+1。
最后一个b出现的编号应该记录最后b出现的位置+1的编号
因为要从第一个b开始用树状数组增加修改多出来的值,然后从最后个b的后一位开始减去增加的值,(如果从最后个b开始的话,那么a-b这条边也被减去了)
题目还是有点复杂的,毕竟初学,但是收获颇大
AC代码地址:http://paste.ubuntu.net/12304944/