ST表

ST表

ST算法可以在 \(O(N \log N)\) 时间的预处理后,以 \(O(1)\) 的时间复杂度在线回答区间最值问题

状态转移

一个序列的子区间个数显然有 \(N^2\) 个,根据倍增思想,我们可以在这个空间里选择一些 \(2\) 的整数次幂的位置作为代表值。

状态:\(f[i][j]\) :数组 \(a\) 在子区间 \([i, i+2^j-1]\) 中的最大值

初值:显然 \(f[i][0] = a[i]\),即 \(a\) 在子区间 \([i, i]\) 中的最大值

状态转移:\(f[i][j] = max(f[i][j-1],f[i+2^{j-1}][j-1])\)

Example:

假设现有一数组 \(a = \{ 5,3,7,9,6,4,1,2\}\),可以得到如下表格

\(i\) \(a\) \(ST[i][0]\) \(ST[i][1]\) \(ST[i][2]\) \(ST[i][3]\)
\(1\) \(5\) \(5\) \(5\) \(9\) \(9\)
\(2\) \(3\) \(3\) \(7\) \(9\) \
\(3\) \(7\) \(7\) \(9\) \(9\) \
\(4\) \(9\) \(9\) \(9\) \(9\) \
\(5\) \(6\) \(6\) \(6\) \(6\) \
\(6\) \(4\) \(4\) \(4\) \ \
\(7\) \(1\) \(1\) \(2\) \ \
\(8\) \(2\) \(2\) \ \ \

若想求出 \(ST[1][2]\) 的值,即区间 \([1,4]\) 中的最值

我们可以取 \(ST[1][1]\)\(ST[3][1]\) 的最大值作为 \(ST[1][2]\) 的值

因为 \(ST[1][1]\) 代表区间 \([1,2]\) 中的最大值,\(ST[3][1]\) 代表区间 \([3,4]\) 中的最大值

取两个数的最大值,即为区间 \([1,4]\) 中的最值

由此可得状态转移方程:\(f[i][j] = max(f[i][j-1],f[i+2^{j-1}][j-1])\)

照应上述例子,\(3 = 1 + 2^{2-1}\),恰好满足。

初始化

根据状态转移方程,可得如下代码:

void ST_pre()
{
    for(int i=1;i<=n;i++)
        ST[i][0]=a[i];
    int t=log2(n)+1;
    for(int j=1;j<=t;j++)
        for(int i=1;i<=n-(1<<j)+1;i++)
	    ST[i][j]=max(ST[i][j-1],ST[i+(1<<(j-1))][j-1]);
}

查询

当询问区间 \([l,r]\) 中的最值时,令区间长度 \(x = r-l+1\)

我们可以先计算出一个\(k\),满足 \(2^k \leq x \leq 2^{k+1}\)

即将区间分为 \(x\) 小的 \(2\) 的最大整数幂剩下的部分

再将剩下的部分进行分解,直到不能再分为止

然后依次返回每个区间的最值,取最大值即可

int ST_query(int l,int r)
{
    int k=log2(r-l+1);
    return max(f[l][k],f[r-(1<<k)+1][k]);
}
posted @ 2022-10-24 21:55  Lan_Sky  阅读(66)  评论(0)    收藏  举报