ST算法和ST表
目录
一、解决什么类型的问题
二、ST算法原理
三、实现
一、解决什么类型的问题
对于一个数列a,现要进行k次操作:
给定l和r,输出l和r之间的最大值
很容易想暴力,总时间差不多是
二、ST算法原理
因为任意一个自然数都可以被表示为数个2的n次幂的和(废话),所以我们可以通过在输入数列a时进行一次预处理,将数列分为n个小段,对于每一个小段求出最大值(有点像最优子结构的意思),最后合并成原来的数列。你可能会觉得他会漏掉一部分区间,但是任意区间都可以通过两个二次幂的区间运算得到
比如[1,3]=max([1,2],[2,3])
三、实现
先设f[i][j]表示数列a中从i开始的2^j个数的最大值,其中f[i][0]=a[i],因为f[i][0]表示a[i]到a[i]间的最大值。
在预处理,也就是地推时,我们使用倍增的思想
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
即从=以f[i][j] 为分界线,将他左边分成一段,再把他的右边分成一段.
f[i][j-1]的i表示i为起点,是长度。
f[i+(1<<(j-1))][j-1]中:
i+(1<<(j-1)) 表示右区间的起点也就是左区间的终点+1
来看一道模板题:
P3865 【模板】ST 表 && RMQ 问题 - 洛谷
先写读入和输出部分
int main(){n=read();m=read();for(int i=1;i<=n;++i){a[i]=read();}pre_work();//预处理函数for(int i=1;i<=m;++i){int a,b;a=read();b=read();printf("%d\n",ST_query(a,b));}return 0;
}
定义 ST_query(a,b)返回[a,b]区间的最大值
预处理
接下来是最重要的预处理函数
void pre_work(){for(int i=1;i<=n;++i){f[i][0]=a[i];}int t=log(n)/log(2)+1;for(int j=1;j<t;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]);}}
}
首先确定dp边界,就上上面说的f[i][0]的最大值就是a[i]。赋初值
随后确定循环次数,数列的总长度为n,使用 计算出需要处理的区间长度的最大二进制幂次加 1,实际意义表示原来的数列长度分解为k个2的n次方后的2的指数,具体来说是
n=8,计算得k=4,表示循环变量会经过中的0,1,2,3,以此保证以
为长度划分序列不会超过序列的原长度。
查找
int ST_query(int l,int r){int k=log(r-l+1)/log(2);return max(f[l][k],f[r-(1<<k)+1][k]);
}
计算出一个k,使得 不能超过数列的最大长度
但是两段之间不能有空隙,如果漏掉了一些数,就不能保证是最大值
所以有空隙就不行,但是有重叠没关系
但现在我们知道1区间的起点,但问题是我们并不知道2区间的长度。如果从1区间的起点加上1区间的长度,有极大概率超出原数列的长度。所以我们采取一种措施,就是从数列的末端向前移动个单位,以此查找
Talk is cheap,show me the code
AC Code:
#include<bits/stdc++.h>
using namespace std;
int const N=1e5+10;
int a[N],f[N][32];
int n,m;
int read(){int s=0,fl=1;char w=getchar();while(w>'9'||w<'0'){if(w=='-')fl=-1;w=getchar();}while(w<='9'&&w>='0'){s=s*10+(w^48);w=getchar();}return fl*s;
}
void pre_work(){for(int i=1;i<=n;++i){f[i][0]=a[i];}int t=log(n)/log(2)+1;for(int j=1;j<t;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]);}}
}int ST_query(int l,int r){int k=log(r-l+1)/log(2);return max(f[l][k],f[r-(1<<k)+1][k]);
}int main(){n=read();m=read();for(int i=1;i<=n;++i){a[i]=read();}pre_work();for(int i=1;i<=m;++i){int a,b;a=read();b=read();printf("%d\n",ST_query(a,b));}return 0;
}