ST表是什么:
st表其实就是一个二维数组,利用了倍增的思想。st[i][j]维护区间[i,i+2^j-1]内的最大值或最小值。
ST表一般用来干什么:
st表一般用来解决RMQ(区间最值)问题,但是不支持修改操作。如果需要修改,请直接用线段树。
时间复杂度 | 预处理 | 查询 |
st表 | O(nlgn) | O(1) |
线段树 | O(nlgn) | O(lgn) |
怎么构建ST表:
前面已经提到过,st[i][j]维护区间[i,i+2^j-1]内的最大值或最小值,而区间[i,i+2^j-1]可以分成两部分来表示,即:[i,i+2^(j-1)-1]和[i+2^(j-1),i+2^(j-1)+2^(j-1)-1],其分别对应st[i][j-1]和st[i+2^(j-1)][j-1],因此若维护的是区间最大值,我们可以得到一个状态转移方程:st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]),那么就有了初始化操作:(至于上界的设置 我们下面再解释)
inline void init()
{
int MAX=log2(n);
for(int i=1;i<=n;i++)
st[i][0]=a[i];//2^0-1=0 即st[i][j]维护的是区间[i,i]的值 即本身
for(int j=1;j<=MAX;j++)//外层循环是j
for(int i=1;i<=n;i++)
if(i+(1<<j)-1<=n)
st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
可以加一个小优化:
inline void init()
{
int MAX=log2(n);
for(int i=1;i<=n;i++)
st[i][0]=a[i];//2^0-1=0 即st[i][j]维护的是区间[i,i]的值 即本身
for(int j=1;j<=MAX;j++)//外层循环是j
{
for(int i=1;i<=n;i++)
{
if(i+(1<<j)-1<=n)
st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
else
break;
}
}
}
怎么查询:
先看一个定理,2^(log2(a))>a/2。(log2(a)是向下取整的)这个还是比较好证明的,我们设a=2^(k-1),b=2^k,那么log2(a)=k-1,log2(b)=k,显然对于a和b都满足上式,那么对于区间(a,b)的所有数x,均有log(x)=k-1,那么2^(log2(x))=2^(k-1)=a,而b=2*a,有x<b,即x/2<a,也即2^(log2(x))=a>x/2。有了这个结论就好办了。我们设要查询的区间的左右端点分别为l和r,那么该区间长度len=r-l+1,由上述结论我们得:2^(log2(len))>len/2,若设t=log2(len),那么区间[l,l+(1<<t)-1]和[r-(1<<t)+1,r]就可以完全覆盖区间[l,r],那么区间[l,r]的最大值就等于:max(st[l][t],st[r-(1<<t)+1][t])。
inline void query(int l,int r)
{
int len=r-l+1;
int t=log2(len); //2^(log2(a))>a/2
printf("%d\n",max(st[l][t],st[r-(1<<t)+1][t]));
}
就这么多了~