专栏文章

题解:P3865 【模板】ST 表 && RMQ 问题

P3865题解参与者 2已保存评论 2

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
2 条
当前快照
1 份
快照标识符
@mipcjpsk
此快照首次捕获于
2025/12/03 09:47
3 个月前
此快照最后确认于
2025/12/03 09:47
3 个月前
查看原文

算法说明

这是一个非常经典的 ST 表问题。我们可以看到限制时间可知,这个模版题对我们的查询复杂度需要控制在 O(1)O(1),所以我们可以分如下几步。ST 表一般分为建表和查询两步。

第一步-建表

第一步,建表,写 ST 表首先是要建表,根据题可知,他要求我们求的是区间最大值,那么我们需要建的是最大值的 ST 表。具体实现代码如下,时间复杂度是 O(NlogN)O(N \cdot \log N),其实在这里有一点倍增法的思路在内了。

第一步正确性说明

而在这里,需要一点点的正确性的证明:如果该区间长度为 lenlen,那么也存在 2p=len2^p=len,那么有 k=pk=\lfloor p \rfloor,而此时显而易见 2klen2^k \le len,那么 2k+1len2^{k+1} \ge len。因此可以得出 k=log2lenk= \lfloor \log_2 len \rfloor
那这很显然会有一定区间重合,但这并不影响答案。

建表步骤代码实现

CPP
void jb(){
	for(int i=1;i<=n;i++) F[i][0]=a[i];
	int k=log2(n);
	for(int j=1;j<=k;j++){
		for(int i=1;i<=n-(1<<j)+1;i++){
			F[i][j]=max(F[i][j-1],F[i+(1<<(j-1))][j-1]);
		}
	}
} 

第二步-查询

第二步,查询,我们可以通过一个公式去得出他的结果。我们可以知道他可以在 O(1)O(1) 的复杂度的查询时间,且我们可以通过 max(F[l][k],F[r-(1<<k)+1][k]); 进行一次求值。那么我们就需要考虑 kk 的取值了。由于实在区间内所以必有左区间与右区间,假设左区间位置为 ll,右区间为 rr,则我们可以得出区间内个数有 rl+1r-l+1,那么我们可以立即得出代码。

证明

这里稍微证明了一下,我们在不断右移时知道仅剩下一位,那么右移次数即为答案,每次除以 22 后进行向下取整,相当于右移一位,那么由于前面在建表,也就是预处理,就已经求出来了,所以直接进行调用再加 11 即可。

查询步骤代码实现

CPP
int cx(int l,int r){
	int k=log2(r-l+1);
	return max(F[l][k],F[r-(1<<k)+1][k]);
}

整合代码

根据上述两步,那么我们可以得出下列代码,注意建表是需要把所有待处理的数输入好以后在进行建表。
CPP
#include<bits/stdc++.h>
using namespace std;
int n,m;
int F[100005][25];
int a[100005];
int x,y;
void jb(){
	for(int i=1;i<=n;i++) F[i][0]=a[i];
	int k=log2(n);
	for(int j=1;j<=k;j++){
		for(int i=1;i<=n-(1<<j)+1;i++){
			F[i][j]=max(F[i][j-1],F[i+(1<<(j-1))][j-1]);
		}
	}
} 
int cx(int l,int r){
	int k=log2(r-l+1);
	return max(F[l][k],F[r-(1<<k)+1][k]);
}
int main() {
	cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i];
    jb();
    for(int i=1;i<=m;i++){
        cin>>x>>y;
        cout<<cx(x,y)<<endl;
    }
     return 0;
}

快读

但可以那份发现超时了,具体请见链接,那么我们可以发现,我们没法在该算法上的时间复杂度进行优化,只能在输入输出进行优化,由于题目所给的函数不会用,所以我将使用如下 33 行代码进行优化输入输出。
CPP
   ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
所以将这三行贴入那份代码,我们可以就可以通过本题了。务必要注意的是,需要在主函数的前三行去贴,才能保证每次输入均能被优化。这三行加入后,即可得到完美的正解。提示的已经很明显了。那么就可以得出如下代码。

AC 代码

CPP
#include<bits/stdc++.h>
using namespace std;
int n,m;
int F[100005][25];
int a[100005];
int x,y;
void jb(){
	for(int i=1;i<=n;i++) F[i][0]=a[i];
	int k=log2(n);
	for(int j=1;j<=k;j++){
		for(int i=1;i<=n-(1<<j)+1;i++){
			F[i][j]=max(F[i][j-1],F[i+(1<<(j-1))][j-1]);
		}
	}
} 
int cx(int l,int r){
	int k=log2(r-l+1);
	return max(F[l][k],F[r-(1<<k)+1][k]);
}
int main() {
 ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
	cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i];
    jb();
    for(int i=1;i<=m;i++){
        cin>>x>>y;
        cout<<cx(x,y)<<endl;
    }
     return 0;
}

ST 表补充说明

但是做 ST 表时,需要注意,就需要静态区间的,如果有变动,就非常占时间,因为动态可能导致数会发生变化,从而导致 ST 表需要重新建表,但因为题目说是静态区间的,所以可以使用 ST 表完成本题。

评论

2 条评论,欢迎与作者交流。

正在加载评论...