【树论】RMQ问题和ST表

发布时间 2023-08-24 14:40:03作者: GHIvan

RMQ问题

RMQ(Range Maximum/Minimum Query)问题,即区间最值问题。

一般是多次询问,对时间复杂度要求高,一般需要 \(O(logn)\)\(O(1)\) 复杂度

ST表

p[i][j] 是以i为起点,连续 \(2^j\) 个数字的最大值(是一个递推表)

3 9 4 2 1 6 8 5
p[i][0] 3 9 4 2 1 6 8 5
p[i][1] 9 9 4 2 6 8 8 -
p[i][2] 9 9 6 8 8 - - -
p[i][3] 9 - - - - - - -

优缺点

优点:可查询,可在末尾插入删除
缺点:不能修改

实现

递推

\(p(i,j)=\max(p(i,j-1),p(i+2^{j-1},j-1))\)

查询

令 k = r-l+1;
int t = log2(k);
ans = max(p[l][t], p[r-(1<<t)+1][t]);

复杂度

\(O(logn+q)\)

代码

$\color{green}\text{点击查看代码}$
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#pragma GCC optimize (3) // O(3)
using namespace std;

int p[100010][20], lg[100010];

inline int read()
{
	int x=0,f=1; char ch=getchar();
	while (ch<'0'||ch>'9') { if (ch=='-') f=-1; ch=getchar(); }
	while (ch>='0'&&ch<='9') { x=x*10+ch-48; ch=getchar(); }
	return x*f;
}

int main()
{
	ios::sync_with_stdio(false);
	int n, m;
	n=read();m=read();
	for(int i = 1;i <= n;i++) p[i][0]=read();
	for(int j = 1;(1<<j) <= n;j++)
		for(int i = 1;i+(1<<j-1) <= n;i++)
			p[i][j] = max(p[i][j-1], p[i+(1<<j-1)][j-1]);
	lg[1] = 0;
	for(int i = 2;i <= n;i++) lg[i] = lg[i>>1] + 1;
	while(m--)
	{
		int l, r;
		l=read();r=read();
		int t = lg[r-l+1];
		printf("%d\n",max(p[l][t], p[r-(1<<t)+1][t]));
	}
	return 0;
}

技巧-快速读入

inline int read()
{
	int x=0,f=1; char ch=getchar();
	while (ch<'0'||ch>'9') { if (ch=='-') f=-1; ch=getchar(); }
	while (ch>='0'&&ch<='9') { x=x*10+ch-48; ch=getchar(); }
	return x*f;
}

背下来就好了