数据结构学习笔记——ST表

ST表

导入

RMQ(Range Mininum/Maxnum Query):即区间最值查询。对于长度为(N)的数组(A),有若干次询问(RMQ(i,j)),回答下标区间([i,j])内的最大/最小值。

ST表可以很好地解决此类问题,预处理复杂度(O(NlogN)),查询(O(1))

预处理

(A[i])是要求区间最值的数组,(F[i][j])表示从第(i)个数起连续(2^j)个数中的最大值。

显然初始化时要令(F[i][0]=A[i])

除初始状态外,(F[i][j])表示的区间必有偶数个数字。将其均分为两段,一段为(F[i][j-1]),另一段为(F[i+2^{j-1}][j-1])。注意两段均包含(2^{j-1})个数字。

则状态转移方程(以求区间最大值为例)

[F[i][j]=max(F[i][j-1],F[i+2^{j-1}][j-1]) ]

从而预处理出数组中所有长度为(2^j)((j=0,1...log_2N(向下取整)))的区间上的最值。

    //其实就是区间DP
	for (int j = 1; j <= lg[n]; j++) {
        //枚举区间长度
        for (int i = 1; i + (1 << j) - 1 <= n; i++) {
            //枚举左端点i,区间长度为1<<j,则右端点就是i+(1<<j)-1,显然右端点不能超过n
            f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
        }
    }

查询

假设需要查询的区间为([l,r]),注意到区间重复是不影响结果的。如查询([1,5]),则查询(F[1][2])(区间([1,4])的最大值)和(F[2][2])(区间([2,5])的最大值)即可。

换句话说,我们需要找到一个合适的(k),使得(l)起往右(2^k)个数不会超过(r),且从(r)起往左(2^k)个数不会超过(l),这样就可以从(F[l][k])(F[r-2^k+1][k])中得到区间([l,r])的最值。

这个(k)显然和区间长度有关。查询区间的长度为(r-l+1),取(k=log_2(l-r+1))(向下取整),则这个(k)就是我们要找的合适的(k)

则有:

[RMQ(l,r)=max(F[l][k],F[r-2^k+1][k]) ]

    for (int i = 1; i <= m; i++) {
        int l, r; cin >> l >> r;
        int k = lg[r - l + 1];
        cout << max(f[l][k], f[r - (1 << k) + 1][k]);
    }

另外(log_2i)的值同样可以预处理得出。(和倍增LCA的lg预处理不一样,不要混淆!这里是真正的(log_2i),那边是(log_2i+1)

    lg[0] = -1;
    for (int i = 1; i <= n; i++) {
        lg[i] = lg[i / 2] + 1;
    }

完整代码

const int maxn = 1e5 + 100;
int a[maxn], f[maxn][22], lg[maxn];
int main() {
    lg[0] = -1;
    n = read(); m = read();
    //洛谷模板题时间卡得很紧,全部用快读
    for (int i = 1; i <= n; i++) {
        a[i] = read();
        lg[i] = lg[i / 2] + 1;
    }
    for (int i = 1; i <= n; i++) {
        f[i][0] = a[i];
    }
    for (int j = 1; j <= lg[n]; j++) {
        //枚举区间长度
        for (int i = 1; i + (1 << j) - 1 <= n; i++) {
            //枚举左端点i,区间长度为1<<j,则右端点就是i+(1<<j)-1,显然右端点不能超过n
            f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
        }
    }
    for (int i = 1; i <= m; i++) {
        int l, r;
        l = read(); r = read();
        int k = lg[r - l + 1];
        printf("%d
", max(f[l][k], f[r - (1 << k) + 1][k]));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/streamazure/p/13889947.html