赞
踩
目录
RMQ (Range Minimum/Maximum Query)即区间最值查询问题指:有一组数据和若干个查询,要求在短时间内回答每个查询[ l ,r ] 内的最值。
ST表(Sparse Table,稀疏表),主要应用倍增思想,是一种用于解决可重复贡献问题的数据结构。它通过预处理给定数组,创建一个二维表格,使得任何区间的最小/最大值查询都可以在常数时间内完成。ST表特别适合于静态数据:当数列不经常改变时,它是最有效的。可以实现O(nlogn)预处理、O(1)查询。主要用于解决RMQ问题。
可重复贡献问题是指在某些特定的数学运算中,当运算的性质满足一定条件时,即使是在包含重复部分的区间内进行询问,所得到的结果仍然是相同的问题。这种问题的特点是,它们可以通过预处理所有可能的区间,然后在查询时直接返回预处理的结果来解决。例如,最大值问题和最大公因数问题就是典型的可重复贡献问题,因为它们满足以下性质:
max(x, x) = x
gcd(x, x) = x
这些性质意味着,对于任何给定的数 x
,其自身与其他任何数的最大值或最大公因数仍然是 x
本身。因此,当我们需要计算一个区间内的最大值或最大公因数时,可以将区间分割成更小的子区间,并利用这些子区间的结果来快速得出整个区间的答案。
倍增法递推:用两个等长的小区间拼凑成一个大区间。
f[ i ][ j ] 以第 i 个数为起点,长度为的区间中的最大值。
状态转移方程:
- #define _CRT_SECURE_NO_WARNINGS 1
-
- #include <iostream>
- using namespace std;
-
- const int N = 1e5 + 10;
-
- const int M = 20;
-
- int f[N][M];
-
- int main()
- {
- //预处理ST表
- int n = 0;
- int m = 0;
- for (int i = 0; i < n; i++)
- cin >> f[i][0];
- for (int j = 1; j <= M; j++) //枚举区间长度
- for (int i = 1; i + (1 << j) - 1 <= n; i++) //枚举起点
- f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
-
- return 0;
- }
区间终点:
假如n = 6
区间长度倍增:1,2,4,8……
f[ i,0 ]:[ 1 1 ][ 2 2 ][ 3 3 ][ 4 4 ][ 5 5 ][ 6 6 ]
f[ i,1 ]:[ 1 2 ][ 2 3 ][ 3 4 ][ 4 5 ][ 5 6 ]
f[ i,2 ]:[ 1 4 ][ 2 5 ][ 3 6 ]
对查询区间[ l,r ]做分割、拼凑。
区间长度的指数:
k = 0:{1}
k = 1:{2,3}
k = 2:{4,5,6,7}
k = 3:{8,9,10,11,12,13,14,15}
通过观察可以发现:
即区间[ l,r ]必可以用两个长度为的区间重叠拼凑。
即
- int l = 0;
- int r = 0;
- for (int i = 1; i <= m; i++)
- {
- scanf("%d %d", &l, &r);
- int k = log2(r - l + 1); //区间长度指数
-
- printf("%d\n", max(f[l][k], f[r - (1 << k) + 1][k]));
- }
[1,4] -> [1,4] + [1,4]
[1,5] -> [1,4] + [2,5]
[1,6] -> [1,4] + [3,6]
[1,7] -> [1,4] + [4,7]
总结:凡是符合结合律且可重复贡献的信息查询都可以使用ST表。显然最大值、最小值、最大公因数、最小公倍数、按位或、按位与都符合这个条件。如果涉及区间修改操作,就要使用线段树解决了。
luogu:P3865
题目链接:P3865 【模板】ST 表
这是一道 ST 表经典题——静态区间最大值
给定一个长度为 N 的数列,和 M 次询问,求出每一次询问的区间内数字的最大值。
第一行包含两个整数 N,M,分别表示数列的长度和询问的个数。
第二行包含 N 个整数(记为 ai),依次表示数列的第 i 项。
接下来 M 行,每行包含两个整数
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。