突然想写博客了,刚好把这两天学的东西巩固一下,看着自己半年前的作品真是触目惊心,代码品质极差,文章冗长又半天戳不到重点,不过还是留在那里做个警醒吧。
一、ST表的作用
用于以常数倍的速度查询某个区间的特定值,这个值的选取规则是要符合区间加法,例如最值。缺点在于它和差分数组一样,只支持离线的。
二、ST表的核心原理
设一数为 len , 我们知道对于任意个大于零的数都有:2 ^ log(2) len > len / 2 并且 2 ^ log(2) len <= len ,而这个特定数字 2^log(2)len 就是这个算法的核心,原因就是满足上述规则,我们后面就设为 k 。那么现在我们假定一个区间[L,R] ,这个区间的长度为 R - L + 1 , 即 len = (R - L + 1),k = 2 ^ log(2)(R - L+1)。现在我们就问题创建两个新区间[L,L+K] [R-K,R],如图:
我们可以很形象的发现这两个区间的合并起来是直接包含整个区间的,若查找的特定值满足区间加法,则根据相应规则在两区间内做选择即可
三、ST表的实现方法
为了方便上述核心原理中的选择部分,所以我们给区间上的每一个点 x (1<= x <= len)后面2^log(2) k (x + 2 ^ log(2)k - 1<= len)区间做一个预处理,代码的算法利用dp实现,对于某个点 x 如图:
现设计这个dp数组 ,设dp[x][k] 为 x个点后的2 ^ k 区域内的一个特定值(后面为了方便理解,就以最小值为例),对于状态转移方程,我们知道每一个区域都是可以由两部分组成的(2 ^ k = 2 ^ (k-1) + 2 ^ (k-1)),而这个状态转移方程就可以很容易写出来:
四、实现代码:
void prest()
{
LOG[0] = -1;
for(int i = 1 ; i <= n ; ++ i)
{
sta[i][0] = a[i];
LOG[i] = ( ( i & (i-1) ) == 0) ? LOG[i-1] + 1 : LOG[i-1];
}
for(int i = 1 ; i <= LOG[n] ; ++ i)//每一个区间的大小的对数值
for(int j = 1 ; j + (1 << i) - 1 <= n ; ++ j)//可能起点
{
sta[j][i] = min(sta[j][i-1],sta[j+(1 << (i-1))][i-1]);
}//因为每一次状态转移时,前一次的区间值都会参与影响,所以我们以区间值作为外循环
}
int querya(int l , int r)
{
int pos = LOG[(r - l) + 1];//相应对数值
return min(sta[l][pos],sta[r-(1<<pos)+1][pos]);//合并区间并选择
}