浅谈ST表

给定一个长度为n的数列,请小明求出其中m段给定区间中的最大值。

显然当n,m很小的时候这是个暴力枚举的入门题,而n,m很大的时候只会暴力枚举的小明只能哭唧唧。

这时ST表登场了,它可以实现O(nlogn)预处理,O(1)查询,学会了它小明马上笑嘻嘻。

怎样做到O(1)查询呢?我们可以很容易想到令maxx[i][j]表示i到j内区间最大值,状态转移方程即:

maxx[i][j]=max(maxx[i][j-1],a[j]) 

这时候小明不干了:从1到n枚举i和j,这明明是O(n2)预处理嘛!

面对熊孩子小明,聪明的我们又想到在LCA中学习过的倍增,于是maxx[i][j]华丽丽摇身一变成为从i开始2j个数的最大值,这时状态转移方程就变了:

maxx[i][j]=max(maxx[i][j-1],maxx[i+(1<<(j-1))][j-1])

人话翻译一下就是:从i开始2j个数的最大值就是从i开始连续两组2j-1个数的最大值比较取得的最大值。

于是乎j只需从1枚举到log2n而i只需保证(1<<j)+i-1<=n即可(-1是因为从i开始2j个数算i自己啦

小明:脑子会了手不会怎么办?

别怕洛谷有模板:https://www.luogu.com.cn/problem/P3865

 1 #include<bits/stdc++.h>
 2 #define ff(i,s,e) for(register int i=s;i<=e;i++)
 3 #define fff(i,s,e) for(register int i=s;i>=e;i--)
 4 #define inf 0x3f3f3f3f
 5 using namespace std;
 6 inline int read(){
 7     register int x=0,f=1;
 8     register char ch=getchar();
 9     while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
10     while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
11     return x*f;
12 }
13 const int N=1e5+2;
14 int n,m,maxx[N][50];//从i开始2^j个数的最大值 
15 int main(){
16     n=read(),m=read();
17     ff(i,1,n) maxx[i][0]=read(); 
18     ff(j,1,log2(n)){
19         for(int i=1;(1<<j)+i-1<=n;i++){
20             maxx[i][j]=max(maxx[i][j-1],maxx[i+(1<<(j-1))][j-1]);//在从i开始连续两组2^j-1个数的最大值中选最大值 
21         }
22     }
23     int l,r;
24     while(m--){
25         l=read(),r=read();
26         int len=log2(r-l+1);
27         printf("%d\n",max(maxx[l][len],maxx[r-(1<<len)+1][len]));
28     }
29     return 0;
30 }

好的现在小明脑子会了手也会了。

(为什么加上小明整个文风都变得迥异了起来

posted @ 2022-02-16 19:06  专吃小仙女  阅读(80)  评论(0)    收藏  举报