ST表模板
RMQ问题:
RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为n的数列a,回答若干询问RMQ(A,i,j)(i, j<=n),返回数列a中下标在i,j之间的最小/大值。如果只有一次询问,那样只有一遍for就可以搞定,但是如果有许多次询问就无法在很快的时间处理出来。在这里介绍一个在线算法。所谓在线算法,是指用户每输入一个查询便马上处理一个查询。该算法一般用较长的时间做预处理,待信息充足以后便可以用较少的时间回答每个查询。
ST(Sparse Table)算法是一个非常有名的在线处理RMQ问题的算法,它可以在O(nlogn)时间内进行预处理,然后在O(1)时间内回答每个查询。
ST表模板题:洛谷P3865【模板】ST表:https://www.luogu.com.cn/problem/P3865
题目要求:给定一个静态区间,求出区间最大值。
首先,我们需要进行预处理操作,代码如下:
1 void Rmqint() {
2 for(int i = 1;i <= n;i++)
3 st[i][0] = a[i];//从i开始长度为1的区间内最大值就为它本身
4 5 //二重循环求出n个点中起点不同、不同区间中的元素最大值
6 for(int j = 1;(1 << j) <= n;j++)//枚举不同区间大小,位运算1 << j表示2的j次方;我们需要保证区间大小小于数列长度
7 for(int i = 1;i + (1 << j) - 1 <= n;i++)//枚举起点,i + (1 << j) - 1表示终点位置,终点位置不能超越数列末端
8 st[i][j] = max(st[i][j - 1],st[i + (1 << j - 1)][j - 1]);//对两个小区间中最大值较大者即为大区间元素的最大值
9 }
这里我们用一个二维数组st[i][j]来表示从i开始长度为2的j次方的区间中元素的最大值,因为起点为i,长度为2的0次方即1的区间内元素最大值就为他本身,所以我们先给st数组赋上初始值;
对于一个长度为2的j次方的区间,我们可以将其拆分为两个长度为2的j - 1次方的小区间:第一个小区间的起点为i,终点为i + 2的j - 1次方 - 1;第二个小区间的起点为i + 2的j - 1次方,终点为i + 2的j次方 - 1。这里用到了二进制拆分的思想。
如图所示:
其次,我们进行的是查询操作,代码如下:
1 int Query(int l,int r) {//l表示区间的左端点,r表示区间右端点
2 int k = 0;
3 while((1 << k) <= r - l + 1){//找出小于等于区间长度的最大小区间长度
4 k++;
5 }
6 k--;//while循环最后k多加了一次,需要减去1
7 return max(st[l][k],st[r - (1 << k) + 1][k]);
8 }
我们用一个整数k来枚举2的指数,通过上面while循环我们可以找出一个满足2的k次方小于等于区间长度的最大整数k,从而使得拆分出的两个小区间完全覆盖大区间,不会有遗漏的元素,有些元素重复考虑也没关系。
找出了最大整数k,我们即能将大区间拆分为两个小区间:第一个区间起点为L,终点为L + 2的k次方 - 1;第二个区间起点为r - 2的k次方 + 1,终点为r。
图解:
PS:两个区间可能有重叠部分,图画得不好
两个核心代码解决了,这道题也就迎刃而解了。完整代码如下:
1 #include <bits/stdc++.h>
2 using namespace std;
3 #define maxn 2 * 1000000
4 int n,m,a[maxn],l[maxn],r[maxn],st[100000][64];//其实2的30次方就够了
5 void Rmqint() {
6 for(int i = 1;i <= n;i++) {
7 st[i][0] = a[i];//从i开始长度为1的区间内最小值就为它本身
8 }
9 //二重循环求出n个点中起点不同、不同区间中的元素最大值
10 for(int j = 1;(1 << j) <= n;j++)//枚举不同区间大小,位运算1 << j表示2的j次方;我们需要保证区间大小小于数列长度
11 for(int i = 1;i + (1 << j) - 1 <= n;i++)//枚举起点,i + (1 << j) - 1表示终点位置,终点位置不能超越数列末端
12 st[i][j] = max(st[i][j - 1],st[i + (1 << j - 1)][j - 1]);//对两个小区间中最大值较大者即为大区间元素的最大值
13 }
14 int Query(int l,int r) {//l表示区间的左端点,r表示区间右端点
15 int k = 0;
16 while((1 << k) <= r - l + 1){//找出小于等于区间长度的最大小区间长度
17 k++;
18 }
19 k--;//while循环最后k多加了一次,需要减去1
20 return max(st[l][k],st[r - (1 << k) + 1][k]);
21 }
22 void Read() {
23 scanf("%d%d",&n,&m);
24 for(int i = 1;i <= n;i++) {
25 scanf("%d",&a[i]);
26 }
27 for(int i = 1;i <= m;i++) {
28 scanf("%d%d",&l[i],&r[i]);
29 }
30 }
31 int main() {
32 Read();
33 Rmqint();
34 for(int i = 1;i <= m;i++) {
35 printf("%d\n",Query(l[i],r[i]));
36 }
37 return 0;
38 }
完结撒花
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!