稀疏表

    科技2022-08-13  98

    文章目录

    稀疏表构造稀疏表查询范围最大值洛谷P3865 ST表

    稀疏表

    学习稀疏表:https://www.youtube.com/watch?v=c5O7E_PDO4U

    稀疏表(Sparse Table),也叫ST表,用来解决范围最值查询(Range Minimum Query)问题。

    假设有一个数组,我们需要对该数组执行如下操作(以下全部假设数组下标从0开始):

    求一个区间 [ l o w , h i g h ] [low,high] [low,high]内的最大值

    不同的解决方法所花的时间不同:

    解决方法预处理时间查询时间普通数组 O ( 1 ) O(1) O(1) O ( n ) O(n) O(n)动态规划 O ( n 2 ) O(n^{2}) O(n2) O ( 1 ) O(1) O(1)线段树 O ( n ) O(n) O(n) O ( log ⁡ 2 n ) O(\log_{2}n) O(log2n)稀疏表 O ( n log ⁡ 2 n ) O(n\log_{2}n) O(nlog2n) O ( 1 ) O(1) O(1)

    因此,稀疏表在上面4种方式中是最好的。

    构造稀疏表

    假设数组的大小为 N N N

    首先需要一个二维数组 F [ N ] [ ⌊ log ⁡ 2 N ⌋ + 1 ] F[N][\lfloor{\log_{2}N}\rfloor+1] F[N][log2N+1],其中 F [ i ] [ j ] F[i][j] F[i][j]表示范围 [ i , i + 2 i − 1 ] [i, i+2^{i}-1] [i,i+2i1]中的最大值。

    例如 F [ 1 ] [ 2 ] F[1][2] F[1][2]表示范围 [ 1 , 4 ] [1, 4] [1,4]中的最大值,即 1 1 1表示从 1 1 1开始, 2 2 2表示 2 2 = 4 2^{2}=4 22=4个元素,即下标从1开始的4个元素中的最大值。

    构造稀疏表的过程可在 O ( n log ⁡ 2 n ) O(n\log_{2}n) O(nlog2n)时间内完成,思想类似于动态规划。下面是C++的简单实现:

    #include <algorithm> int n; int arr[100000]; int sparse_table[100000][17]; void construct_sparse_table() { for (int i = 0; i != n; i++) { sparse_table[i][0] = arr[i]; } for (int j = 1; (1 << j) <= n; j++) { for (int i = 0; i + (1 << j) - 1 < n; i++) { sparse_table[i][j] = std::max(sparse_table[i][j - 1], sparse_table[i + (1 << (j - 1))][j - 1]); } } }

    循环中的sparse_table[i][j]可以理解为从 i i i开始的 2 j 2^j 2j个元素的最大值。

    它等价于在从 i i i开始的 2 j − 1 2^{j-1} 2j1个元素中的最大值,以及在从 i + 2 j − 1 i+2^{j-1} i+2j1开始的 2 j − 1 2^{j-1} 2j1个元素中的最大值,中的最大的一个值。

    即分成两半(因为总共有 2 j 2^j 2j个元素),然后根据这两半的最大值计算最终的最大值。

    查询范围最大值

    假设范围为 [ l o w , h i g h ] [low, high] [low,high]

    l = h i g h − l o w + 1 l=high-low+1 l=highlow+1(即范围内的元素个数), k = ⌊ log ⁡ 2 l ⌋ k=\lfloor\log_{2}l\rfloor k=log2l

    因此只需要找到范围 [ l o w , k ] [low,k] [low,k] [ l o w + l − 2 k , k ] [low+l-2^k,k] [low+l2k,k]中的最大值即可。

    l − 2 k l-2^k l2k是因为总共有 l l l个元素,已经计算了 2 k 2^k 2k个元素,还差 l − 2 k l-2^k l2k个元素,这时只需要将范围整体往后移动 l − 2 k l-2^k l2k即可。

    下面是C++的简单实现:

    #include <algorithm> #include <cmath> int n; int arr[100000]; int sparse_table[100000][17]; int range_maximum_query(int low, int high) { int l = high - low + 1; int k = static_cast<int>(std::floor(std::log2(l))); return std::max(sparse_table[low][k], sparse_table[low + l - (1 << k)][k]); }

    洛谷P3865 ST表

    利用这个数据结构,可以解决洛谷P3865 ST表,题目链接:https://www.luogu.com.cn/problem/P3865。

    下面是C++的代码:

    #include <algorithm> #include <cmath> #include <iostream> int n, m; int arr[100000]; int sparse_table[100000][17]; inline int read() { int x = 0, f = 1; char ch = getchar(); while (!isdigit(ch)) { if (ch == '-') { f = -1; } ch = getchar(); } while (isdigit(ch)) { x = x * 10 + ch - 48; ch = getchar(); } return x * f; } void construct_sparse_table() { for (int i = 0; i != n; i++) { sparse_table[i][0] = arr[i]; } for (int j = 1; (1 << j) <= n; j++) { for (int i = 0; i + (1 << j) - 1 < n; i++) { sparse_table[i][j] = std::max(sparse_table[i][j - 1], sparse_table[i + (1 << (j - 1))][j - 1]); } } } int range_maximum_query(int low, int high) { int l = high - low + 1; int k = static_cast<int>(std::floor(std::log2(l))); return std::max(sparse_table[low][k], sparse_table[low + l - (1 << k)][k]); } int main() { std::cin >> n >> m; for (int i = 0; i != n; i++) { arr[i] = read(); } construct_sparse_table(); int l, r; for (int i = 0; i != m; i++) { l = read(); r = read(); std::cout << range_maximum_query(l - 1, r - 1) << "\n"; } }
    Processed: 0.008, SQL: 8