教你怎么写神秘的ST表
ST表学习之初出茅庐
\(Luogu\) \(P3856\)
给定一个长度为 N 的数列,和 M 次询问,求出每一次询问的区间内数字的最大值。
对于 100% 的数据,\(1<=N<=10^5\),\(1<=M<=2×10^6,a_1∈[0,10^9]\)
解法:美丽ST表
st表的码量小(不像隔壁种树的,基于动态规划与倍增思想,对于算法学习者很友好
但是这种算法有一种局限性就是只有所有元素满足对于opt运算符有\(x\) \(opt\) \(x\) = \(x\),就比如\(max\), \(x=max(x,x)\)
我们定义\(st[i][j]\)为区间\([i,i+2^j]\)的最大值,居然是dp
喜闻乐见之状态转移方程怎么办?我们可以拆分区间,因为重复的部分不影响最终结果,所以转移更方便,也就是说
\(st[i][j]=max(st[i][j-1],st[i+(1<<j-1)][j-1])\)
这样的话就刚好能覆盖一整个完整区间。
其中的i,显然,为1..n
方程中的j,根据意义,为\([0,\lfloor log_2n \rfloor]\)
循环时必须先枚举j,才能满足递推式,接着枚举i,当最后一个点大于n时就不继续了,j++。
顺便预处理一下\(log_2n\),显然满足递推式:
\(logn_1=0,logn_2=1\)
\(logn_i=logn_{\lfloor \frac{i}{2} \rfloor}+1\)
那么怎么查找呢?
我们就把要查找的区间拆分,同样,重叠部分不影响结果。
\((s[i][log_2(r-l+1)],s[r-2^{log_2(r-l+1)}][log_2(r-l+1)])_{max}\)
$AC$ $CODE$
#include<bits/stdc++.h>
using namespace std;
constexpr int N=1e5+10;
int f[N][30],Logn[N],n,m,l,r,x;
inline void init(){
Logn[1]=0,Logn[2]=1;
for(int i=3;i<=n;i++)Logn[i]=Logn[i/2]+1;
}
inline int rd(){
char c;while(!isdigit(c=getchar()));
int x=(c&15);while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+(c&15);
return x;
}
int main(){
n=rd(),m=rd();
init();
for(int i=1;i<=n;i++)f[i][0]=rd();
for(int j=1;j<=Logn[n];j++){
for(int i=1;i<=n-(1<<j)+1;i++){
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
}
while(m--){
l=rd(),r=rd();
int x=Logn[r-l+1];
printf("%d\n",max(f[l][x],f[r-(1<<x)+1][x]));
}
return 0;
}
待更新,因为能听懂我吃,看完就看个寂寞,建议看题解,有讲的更好的。
只要你愿意的话,\((1<<j)\)可以打表

浙公网安备 33010602011771号