ST表
ST表是一种解决RMQ(区间最值问题)的强有力的工具
它可以做到O(nlogn)预处理,O(1)查询最值。
实现
ST表其实是一种倍增的思想,我们就拿取最大值为例:
开一个二维数组Max,其中Max[i][j]表示从第i位开始,包括第i位在内的2^j个数中最大的数,例如Max[i][1]表示第i个数和第i+1个数中大的那个数。
然后就类似于二分的样子,下一层也是拿两个已得到的区间的最大值作比较,然后存储。
预处理代码
1 for(int j=1;j<=21;j++) 2 { 3 for(int i=1;i+(1<<j)-1<=n;i++) 4 { 5 Max[i][j]=max(Max[i][j-1],Max[i+(1<<(j-1))][j-1]); 6 } 7 }
为什么要先循环j呢?因为我们是一层一层向上搜的,每一层的答案需要下面的基础,所以把j放在外层,然后(1<<(j-1))就是把二进制的1向左移动(j-1)位,也就是2^(j-1)。
查询
查询也比较简单,我们只需要求出k=log2(r-l+1)(求2的k次幂小于这一段区间的值,并且尽可能大),然后得到两个区间,比较就行了
然后取最大值返回就可以了
1 int ST(int l,int r) 2 { 3 int k=log2(r-l+1); 4 return max(Max[l][k],Max[r-(1<<k)+1][k]); 5 }
既然了解了这么多,不如.......(放心,都是模板题)
洛谷P3865 【模板】ST表
1 #include<bits/stdc++.h> 2 #define N 100005 3 using namespace std; 4 int n,m; 5 int Max[N][21]; 6 int ST(int l,int r) 7 { 8 int k=log2(r-l+1); 9 return max(Max[l][k],Max[r-(1<<k)+1][k]); 10 } 11 int main() 12 { 13 cin>>n>>m; 14 for(int i=1;i<=n;i++) 15 { 16 scanf("%d",&Max[i][0]); 17 } 18 for(int j=1;j<=21;j++) 19 { 20 for(int i=1;i+(1<<j)-1<=n;i++) 21 { 22 Max[i][j]=max(Max[i][j-1],Max[i+(1<<(j-1))][j-1]); 23 } 24 } 25 int l,r; 26 for(int i=1;i<=m;i++) 27 { 28 scanf("%d%d",&l,&r); 29 printf("%d ",ST(l,r)); 30 } 31 return 0; 32 }
洛谷P2251 质量检测
1 // luogu-judger-enable-o2 2 #include<bits/stdc++.h> 3 #define N 100005 4 using namespace std; 5 int n,m; 6 int Min[N][21]; 7 int ST(int l,int r) 8 { 9 int k=log2(r-l+1); 10 return min(Min[l][k],Min[r-(1<<k)+1][k]); 11 } 12 int main() 13 { 14 cin>>n>>m; 15 for(int i=1;i<=n;i++) 16 { 17 scanf("%d",&Min[i][0]); 18 } 19 for(int j=1;j<=21;j++) 20 { 21 for(int i=1;i+(1<<j)-1<=n;i++) 22 { 23 Min[i][j]=min(Min[i][j-1],Min[i+(1<<(j-1))][j-1]); 24 } 25 } 26 for(int i=1;i+m-1<=n;i++) 27 { 28 printf("%d ",ST(i,i+m-1)); 29 } 30 return 0; 31 }
洛谷P1816 忠诚
1 #include<bits/stdc++.h> 2 using namespace std; 3 int Min[100005][21]; 4 int n,m; 5 int ST(int l,int r) 6 { 7 int k=log2(r-l+1); 8 return min(Min[l][k],Min[r-(1<<k)+1][k]); 9 } 10 int main() 11 { 12 cin>>n>>m; 13 for(int i=1;i<=n;i++) 14 { 15 scanf("%d",&Min[i][0]); 16 } 17 for(int j=1;j<=21;j++) 18 { 19 for(int i=1;i+(1<<j)-1<=n;i++) 20 { 21 Min[i][j]=min(Min[i][j-1],Min[i+(1<<(j-1))][j-1]); 22 } 23 } 24 for(int i=1;i<=m;i++) 25 { 26 int l,r; 27 scanf("%d%d",&l,&r); 28 printf("%d ",ST(l,r)); 29 } 30 }