洛谷P3865 【模板】ST表

链接:P3865

题目背景
这是一道ST表经典题——静态区间最大值

请注意最大数据时限只有0.8s,数据强度不低,请务必保证你的每次查询复杂度为 O(1)O(1)

题目描述
给定一个长度为 NN 的数列,和 MM 次询问,求出每一次询问的区间内数字的最大值。

输入格式
第一行包含两个整数 N, MN,M ,分别表示数列的长度和询问的个数。

第二行包含 NN 个整数(记为 a_ia ),依次表示数列的第 ii 项。

接下来 MM行,每行包含两个整数 l_i, r_il
​ ,表示查询的区间为 [ l_i, r_i][l

输出格式
输出包含 MM行,每行一个整数,依次表示每一次询问的结果。

输入输出样例
输入 #1
8 8
9 3 1 7 5 6 0 8
1 6
1 5
2 7
2 6
1 8
4 8
3 7
1 8
输出 #1
9
9
7
7
9
8
7
9
说明/提示
对于30%的数据,满足: 1 \leq N, M \leq 101≤N,M≤10

对于70%的数据,满足: 1 \leq N, M \leq {10}^51≤N,M≤10
5

(直接看链接)


对于ST表,主要解决RMQ问题
就是对于任何一个序列的连续区间进行查找最大值和最小值。
ST表的算法复杂度:
预处理:O(nlogn)
查找:O(1)

ST表最主要的部分就是预处理
我们对于2^n个区间进行预处理RMQ区间
定义f[i][j]为第i个值到第i+2^j-1个值的最大值
我们可以枚举j的值,在这个区间内分成两个部分从而进行找到最大值
洛谷P3865 【模板】ST表_第1张图片
则这段转移式:f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]

这样我们便可以预处理出每一个从i到2^j的区间长度

那么在判断最大值中,我们应该如何解决任意区间呢?
若设该段区间大小为L,则设c=log2L;
则2^(c-1) 则L<2^c
则L<2*2^(c-1)
则L/2<2^(c-1)
根据这个不等式的解我们可以得出这个图
洛谷P3865 【模板】ST表_第2张图片
从图中可以看出
在这一个任意区间必定可以化成这两个红色区间的情况
这两个红色区间是i~2^j的区间所以一定知道它的区间最大值(根据f[i][j])
因为这两个区间有交集,两个的并集也是整个大区间,所以整个大的区间的最大值就是两个红色区间的最大值的最大值
因此查找只需要输出
max(f[l][j],f[r-(1<

代码:

#include 
#include 
#include 
using namespace std;
int n,m,f[100001][101];
int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&f[i][0]);
	}
	for (int j=1;j<=(int)(log2(n));j++)
	for (int i=1;i<=n-(1<<j)+1;i++)  //进行ST表预处理
	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;
		scanf("%d%d",&l,&r);
		int k=log2(r-l+1);  //找出log值
		printf("%d\n",max(f[l][k],f[r-(1<<k)+1][k]));
	}
	return 0;
}

你可能感兴趣的:(ST表,RMQ)