LCA-倍增思想 POJ1330 Nearest Common Ancestors

LCA倍增思想
首先是什么是倍增?
清楚一个性质:任何一个数都可以转换为2进制表示,就像任何一个数都可以被十进制表示一样。
比如(9)10=(1001)2
因此9=8+1;
20=16+4;
注意到的是:都是从高位向低位扫描。
看例子:20=16+4;
For(int i=6;i>=0;i–)
{
if(num+2^i>20)continue;
num+=2^i;
}


有了这个思想再说LCA.
维护一个二维数组f[i][j]表示.节点i向上走2^j步到达的节点,也就是i节点的某一个祖先。
f[a][0]表示a的父亲,其中0代表2^0=1,也就是树上该点向上一步。
之后用一个递推式求出所有的f[i][j]。
f[i][j]=f[f[i][j-1]][j-1]
这个公式怎么推出来的呢?
f[i][j] 表示节点i向上走2^j步到达的节点p。
f[i][j-1] 表示节点i向上走2^(j-1)步到达的节点p’。
p’再走多少步就可以到达p?2^j-2^(j-1)=2^(j-1)
因此f[p’][j-1]=p;又因为p’=f[i][j-1];
也就是f[f[i][j-1]][j-1]=f[i][j];

以下的例子需要模拟时可以参考:
9
2 3
3 4
3 1
1 5
5 6
6 7
7 8
8 9
4 9
2
3
这里写图片描述

LCA的步骤举例:
1.首先要将这两个点u和v调到同一深度,这样以后操作都是同深度的。
2.怎么调深度呢?很简单,将他们的深度相减,我们设为dep,那么这个dep的就对应了深一点的那个点需要上升的高度,恩,应该马上能想到,直接用二进制表示深度然后一直爬上去就行了,这就是倍增的思想,log级别
3.同一深度时,我们要同时上升啦~我们继续用倍增思想,依次上升2^k的高度。什么时候上升呢?当然是f[u][k]!=f[v][k]的时候,因为这说明他们的祖先还不同,他们位于2棵子树,所以要上升。并且顺序要从大到小!否则求不到最小的祖先,很容易理解的。
举例:
题目中求节点4和9的LCA,那么先将9上升到与4同一个深度(dep[9]-dep[4]=5),用二进制log2(4)=2;再上升一层:log2(1)=0。

int t=ceil(int(log2(d[x]-d[y])));  
    for(int i=t;i>=0;i--) 
        if (  (d[x]-(1<<i)) >=d[y]) 
                x=f[x][i]; 

4和9的层数都是3之后,判断节点值是否相同,相同返回,不同继续上升上升多少呢?ceil(log2(dep[4]))=1,从根节点向下搜,如果相同则向上的步数2^减一,如果不同则赋值,继续二进制。

for(int i=t;i>=0;i--)
    if (f[x][i]!=f[y][i])  
    {  
          x=f[x][i];  
          y=f[y][i];  
    }  
return f[x][0];

又比如要走15步才能到达相同的点,这时可能先走16步,然后发现相同,再从4结点走到8步这里,发现不同,结点赋值到8步,剩余7步,再走4步,发现不同,赋值,剩余3步,再走2步,发现不同,赋值,剩余1步,再走1步,发现相同,(不赋值),跳出循环,这时取f[x][0];

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值