st表
ST表
ST表的功能很简单
它是解决RMQ问题(区间最值问题)的一种强有力的工具
它可以做到O(nlogn)预处理,O(1)查询最值
算法学习
- ST表是利用的是倍增的思想
我们用 f[i][j] 表示,从 i 位置开始的 2^j 个数中的最大值(同样可以最小值,也可是别的),例如 f[i][1] 表示的是 i 位置和 i+1 位置中两个数的最大值
那么转移的时候我们可以把当前区间拆成两个区间并分别取最大值(注意这里的编号是从1开始的)
- 如何实现?
首先预处理出我的log数组和d数组(位运算更快,看不懂<<和>>百度吧)
log[n]表示log2(n)向下取整,即使得2^log[i]最大且不超过i
d[i]表示2^i(你接下来d[i]的位置总打1<<i也行吧..)
1 log[0]=-1;d[0]=1; 2 for(R int i=1;i<=n;i++)f[i][0]=ri(),log[i]=log[i>>1]+1; 3 for(R int i=1;i<=19;i++)d[i]=d[i-1]*2;
其次可以预处理出,f[i][0]等于这个数字本身。
两个for:
1 for(int j=1;j<=log[n];j++) 2 for (int i=1;i+d[j]-1<=n;i++) 3 f[i][j]=max(f[i][j-1],f[i+d[j-1]][j-1]);
转移方程如上。
什么意思??
对于每个区间(长度大于1)都可以对半分。这个区间的max从两个区间的最大值中取出。
所以,我们先预处理好每个2^i,2^j次方区间的最大值,上图蓝色线表示f[i][j],可以轻松得到方程 f[i][j] = max( f[i][j-1], f[i+2^(j-1)] [j-1] );
查询
假设我们查询区间(l,r)
那么我们算出区间长度len=r-l+1,求出 k=log[len],那么显然2^k<=len
如图,我们求出红色线段和绿色线段最大值中的最大值即可
如下代码,我直接 len=log[r-l+1],当成k看也一样
max(f[l][len],f[r-d[len]+1][len])
当然,以上只是st表常用的应用之一。
练习题稍后补充。
1 #include <iostream> 2 #include<cstdio> 3 #define R register 4 using namespace std; 5 const int N=100000; 6 int n,m; 7 int f[N+5][20],log[N+5],d[20]; 8 inline int ri(){ 9 char c=getchar();int x=0,w=1; 10 while(!isdigit(c)){if(c=='-')w=-1;c=getchar();} 11 while( isdigit(c)){x=(x<<3)+(x<<1)+c-48;c=getchar();} 12 return x*w; 13 } 14 int main(){ 15 int l,r,len; 16 n=ri();m=ri(); 17 log[0]=-1;d[0]=1;//就这么预处理吧。log和d数组前文有交代 18 for(R int i=1;i<=n;i++)f[i][0]=ri(),log[i]=log[i>>1]+1; 19 for(R int i=1;i<=19;i++)d[i]=d[i-1]*2; 20 for(int j=1;j<=log[n];j++)//保证不越界 21 for (int i=1;i+d[j]-1<=n;i++) 22 f[i][j]=max(f[i][j-1],f[i+d[j-1]][j-1]);//预处理 23 while(m--) {//询问 24 l=ri();r=ri(); 25 len=log[r-l+1]; 26 printf("%d\n",max(f[l][len],f[r-d[len]+1][len])); 27 } 28 return 0; 29 }