RMQ问题的ST算法

RMQ问题,即静态查询区间最值的问题,我们假设我们要求最大值.

那么我们的朴素算法查询区间[l,r]如下:

inline int query(int l,int r){
  int maxx=0;
  for (int i=l;i<=r;i++)
    if (a[i]>maxx) maxx=a[i];
  return maxx;
}

我们知道这样查询时间复杂度最坏是O(n)的,效率极低.

我们可以使用线段树,但是我们知道它是用于动态查询的,静态的题干嘛用它呢.

所以我们的ST算法就出来了.

ST算法的思路就是用一个数组例如rmq[i][j]表示第i个开始的1<<j个元素的最大值.

那么我们就可以推出整个数组.

其中rmq[i][j]=max(rmq[i][j-1],rmq[i+(1<<j)][j-1]).

我们就可以写出如下初始化部分:

inline void start(){
  for (int i=1;i<=n;i++)
    rmq[i][0]=a[i];
  for (int j=1;(1<<j)<=n;j++)
    for (int i=1;i+(1<<j)-1<=n;i++)
      rmq[i][j]=max(rmq[i][j-1],rmq[i+(1<<(j-1))][j-1]);
}

各种细节调了半个小时了,太恐怖了,以后还是zkw线段树黑科技比较好.

初始化的时间复杂度很容易得出为O(nlog(n)).

扫描二维码关注公众号,回复: 391160 查看本文章

那么如何查询呢?

查询的时候假设我们查询[l,r],那么我们就先设len=log2(r-l+1).

但是计算机的log是以10为底算的,所以我们要用换底公式变成len=log(r-l+1)/log(2).

注实数运算默认下取整.

然后我们在两个区间[l,l+(1<<len)-1]与[r-(1<<len)+1,r]的最大值.

为什么我们可以在两个有重复的区间内取最值.

因为是最值啊,不影响.

所以得出以下代码:

inline int query(int l,int r){
  int maxx=0;
  int len=log(r-l+1)/log(2);
  return max(rmq[l][len],rmq[r-(1<<len)+1][len]);
}

这个rmq代码很短,很短...

由于我们的log运算是O(1)的,所以整个是O(1).

当然有些时候直接用std的log会被卡常,所以自己手写吧:

inline void lgnum(){
  int i=1;
  for (int j=0;i<N;j++)
    for (;(i<=(1<<j))&&(i<N);i++)
      lg[i]=j;
}

其中N是lg数组处理到的数+1.

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/79624038