ST表
- 注:ST表需要用到倍增算法;ST表实质上是一种DP
ST表的作用
- 快速查询区间最大值和区间最小值
前提:倍增
我们读入了一个长度为n的数列,用a[1]~a[n]存储。然后我们不由分说地先算出数列中第1~n-1个数的 后两个数(包括它自身)中的最大值,比如说,第一个数后两个数的最大值=max(a[1],a[2])。a[n+1]不存在,所以
不用计算a[n]后两个数的最大值,计算到a[n-1]就可以(而且必须)停下来了然后我们再算出数列中第1~n-3个数的 后四个数(包括它自身)中的最大值,可以再枚举一遍,但我们有先前算好的结果可以利用:第一个数后四个数的最大值=max(第一个数后两个数的最大值,第三个数后两个数的最大值)
- 依次类推,第一个数后八个数的最大值=max(第一个数后四个数的最大值,第五个数后四个数的最大值),第一个数后十六个数的最大值=max(第一个数后八个数的最大值,第九个数后八个数的最大值),……
正题:ST表
- 定义一个数组st[n][x]存储这些值,st[i][j]表示第i个数后面的2^j(即1<<j)个数(包括第i个数自身)中的最大值,x可以用符合2^x<=n的最大x值(+10防越界),也可以自己定义一个足够用的值(+10防越界)
- st[i][0] 即为 a[i],可以用 st[i][0] 代替 a[i] 存储数列
for(int j = 1;j <= x; ++j ){
for(int i = 1;i <= n - (1<<j) +1 ; ++ i){
st[i][j] = std::max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
解释
- j的循环在最外面
根据转移方程 st[i][j] = max(st[i][j-1],st[i+(1<<(j-1))][j-1])(i<=n-2^j+1,1<=j<=x),只有算出所有的st[i][j-1],才能计算st[i][j]。
- i<=n-(1<<j)+1
计算a[i]到a[j]之间的最大值,防止越界,最多只能枚举到a[i]~a[n],
此时n-i+1=2^j(即1<<j),i=n-2^j+1,所以1<=i<=n-(1<<j)+1。
使用
- 不断分割查询区间,直到剩下的区间长度为0,并把所有分割数的区间对应的最大值 一 一比较
- j从x向1枚举,直到(1<<j)<=区间长度,分割并结束循环,即每次只分割 大于区间长度的一半小于等于区间长度 的一段
int search(int l,int len,int r)
{
if(l>r) return -0x3f3f3f3f;
if(len<=0) return -0x3f3f3f3f;
for(int i=25;i>=0;--i){
if( (1<<i) <=len )
return std::max(t[l][i],search(l+(1<<i),len-(1<<i),r));
}
}
拓展
- 最小值当然也可以,注意search查询越界返回的极小值换成极大值就行