倍增小总结

  因为考试倍增题挂了一个long long,被迫写总结QAQ

  ●何谓倍增法

  简单的说就是每次步数是原来的两倍,一般用数组f[i][k]表示在i这个点走了2^k步之后的结果。

  ●倍增法的用处?

  倍增法多用于数列或者树上,进行一段较大的区间查询,把每次查询的时间由O(n)降到O(logn)。

  ●倍增法的常见应用

  一、RMQ 算法

  这个算法其实可以说是个dp了。

  1.简单介绍

  大致是在一段很长序列上进行区间查询,用st[i][k]表示从i开始后2^k个数中的最大/小值或者和。这个数组叫做st表,建立的话是O(nlogn)的时间,但查询只需要O(1)。

  2.对比数据结构

  相比于线段树来说,不仅代码更好写,并且时间更优,因为线段树常数大且每次操作都是logn。但线段树可以支持很多操作,还可以修改之类的,就很屌了。

  比上树状数组么。。

  树状数组常数也很小,并且支持修改操作,但它有一个致命的BUG,那就是它只能查询与前缀相关的东西,譬如前缀和之类的。

如果查询区间和,直接用前缀和减去就好了,但是如果查询区间最大值,那么它就没有任何办法了。

  3.简单建立st表

  确实很简单,大致参照一个dp思路

  i~i+2^k的区间信息可由i+2^(k-1)~i+2^(k-1)+2^(k-1)和i~i+2^(k-1)合并得到。st数组定义参照介绍。

for(int k=1;(1<<k)<=n;k++)

for(int i=1;i+(1<<j)-1<=n;i++)

st[i][k]=min(st[i][k-1],st[i+(1<<(k-1))][k-1]);

  

  值得注意的是,由于处理2^k区间长度时要用到i和i+2^(k-1)点的信息,所以先枚举k。

  4.查询没什么好提的,根据定义来就行

int rmq(int a,int b){

if(a>b)swap(a,b);

int t=(int)log2(b-a+1);

return min(st[a][t],st[b-(1<<t)+1][t]);

}

  

 

  5.简单的小优化

  由于上述的2^i和log2(i)都需要计算,而log2函数的常数是比较大的,所以可以根据幂和log的性质预处理出mi[]和lg[]数组。

此外,RMQ算法在后缀数组中用的还是比较多的。

  二、树上倍增

  1.寻找爸爸

  树上倍增这个东西呢,一般是用来求lca的,单次查询时间logn,具体的描述有点长,就不写了。

  还是大致提一下。关键是建立fa[i][k]表示i点向上数2^k个父亲是谁。查询lca(x,y)的时候得到x,y的深度,帮它们两个提到同一深度,再同时向上提找爸爸就好了。具体有些细节不再赘述。

  挂一波代码。

void dfs(int u,int dad,){

    p[u][0]=dad;
    
    d[u]=d[dad]+1;

    for(int i=1;(1<<i)<=d[u];i++)

    p[u][i]=p[p[u][i-1]][i-1];

   for(int i=hd[u];i;i=e[i].next)

    if(e[i].v!=dad)dfs(e[i].v,u,e[i].w);

}

 

int lca(int x,int y){

    if(d[x]>d[y])swap(x,y);

    for(int i=20;~i;i--)

    if(d[x]<=d[y]-(1<<i))y=p[y][i];

    if(x==y)return x;

    for(int i=20;~i;i--){

    if(p[x][i]==p[y][i])continue;

    x=p[x][i];y=p[y][i];

    }

    return p[x][0];

}
View Code

相关文章:

  • 2022-02-12
  • 2022-12-23
  • 2021-08-30
  • 2021-08-08
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2021-07-09
  • 2022-12-23
  • 2021-07-01
  • 2022-02-18
  • 2022-02-19
  • 2021-06-18
  • 2022-01-18
相关资源
相似解决方案