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.