倍增
倍增是把时间复杂度为
的一个操作变为
的操作。
它与分治的思想类似,时间复杂度也类似。
它们的区别如下:
分治是将一个问题分成若干个子问题,最后的答案由子问题的答案合并得到。
倍增是将一个需要执行多次的操作分解,操作的结果直接由两个子操作的结果得到。
例子
最常见的例子就是在树上倍增。
每个询问要求节点
向上跳
次到达的节点编号。
若每次暴力向父亲节点跳,一共跳
次,时间复杂度特别大。
这时就要使用倍增。
如何实现
维护每个节点向上跳可到达的节点编号。
设
表示节点
向上跳
次到达的节点编号。
所有的
自然就为
(
的父亲节点),其余的如何转移?
我们可以发现,向上跳
次就相当于是先向上跳
次,再向上跳
次。
于是我们不难得到转移方程:
void dfs(int i,int fa)
{
f[i][0]=fa;
for(j=1;j<=log(n);j++) f[i][j]=f[f[i][j-1]][j-1];
for(j=last[i];j;j=next[j]) if(a[j]!=fa) dfs(a[j],i);
}
怎么跳?
类似十进制转二进制的方式,从大到小枚举
,如果
就把
跳到
,并且把
。
for(j=log(n);j>=0;j--)
{
if(s>=p[j])
{
x=f[x][j];
s-=p[j];
}
}
总结
可以用倍增实现的还有很多,如树上最大值、树上两点的最近公共祖先(LCA)等,对解题有很大帮助。
相信你已经对倍增了解不少了。要时刻记得,算法是死的而人是活的,必须学会灵活变通,找到题目突破口,才能顺利解题!