浅谈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 }
好的现在小明脑子会了手也会了。
(为什么加上小明整个文风都变得迥异了起来
浙公网安备 33010602011771号