模板:ST表

解决rmq问题,区间最值的离线做法,写法比线段树和树状数组更加简单

构造O(nlog(n));

查询O(1);

只要运用倍增思想

构造ST表O(nlog(n)):

 

1 void ST_init(int n){
2     for (int i=1;i<=n;i++)dp[i][0]=a[i];
3     int len=(int)(log((double)(n))/log(2.0));
4     for (int j=1;j<=len;j++)
5         for (int i=1;i<=n;i++){
6             dp[i][j]=Max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
7         }
8 }
View Code

 

dp[i][j]:表示从a[i]开始的2^j个元素的目标值;

 

查询O(1):

 

1 int ST_query(int l,int r){
2     int k=int (log((double)(r-l+1))/log(2.0));
3     return Max(dp[l][k],dp[r-(1<<k)+1][k]);
4 }
View Code

 

查询区间(l,r)之间的最值:k为log2(区间长度);dp[l][k]与dp[r-(l<<k)+1][k]覆盖了整个区间,取两者较优值即可

 

完整代码:

 1 #include<cmath>
 2 #include<iostream>
 3 #include<stdio.h>
 4 #include<cstring>
 5 using namespace std;
 6 const int NUM=2e6;
 7 //rmq离线做法ST    在线做法 线段树 树状数组
 8 //ST O(nlog(n))构造  O(1)查询
 9 int a[NUM],n,m,dp[NUM][20];
10 //dp[I][J]表示从I位开始的2^j个数的最小/大值
11 inline int read()
12 {
13     int x=0,f=1;char ch=getchar();
14     while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
15     while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
16     return x*f;
17 }  
18 int Max(int x,int y){
19     return x<y?y:x;
20 }
21 void ST_init(int n){
22     for (int i=1;i<=n;i++)dp[i][0]=a[i];
23     int len=(int)(log((double)(n))/log(2.0));
24     for (int j=1;j<=len;j++)
25         for (int i=1;i<=n;i++){
26             dp[i][j]=Max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
27         }
28 }
29 int ST_query(int l,int r){
30     int k=int (log((double)(r-l+1))/log(2.0));
31     return Max(dp[l][k],dp[r-(1<<k)+1][k]);
32 }
33 int main(){
34     n=read();m=read();
35     for (int i=1;i<=n;i++)a[i]=read();
36     ST_init(n);
37     for (int i=1;i<=m;i++){
38         int l=read();int r=read();
39         printf("%d\n",ST_query(l,r));
40     }
41 }
View Code