ST表

1.什么是 RMQ & ST?

RMQ 是一种区间最值查询的问题,我们珂以用线段树或树状数组来解决,但今天我们要用的是——ST表。

要注意的是,ST表只珂以用于静态区间求最值,而动态区间就只能用线段树或树状数组了。

2.ST 算法思想简述

ST 算法使用了 DP 的思想。

我们用 \(f(i,j)\) 表示以 \(i\) 为起点,连续 \(2^j\) 个数中的最大值。例如 \(f(3,2)\) 就表示以 \(3\) 为起点,连续 \(2^2\) 个数中的最大值,即第 \(3\) 个数到第 \(6\) 个数的最大值。

1.预处理

记原数组为 \(a\),因为 \(2^0=1\),所以 \(f(i,0)=a_i\)

对于 \(j\ne0\) 的情况,我们将其分为长度相等的两部分:\(f(i,j-1)\)\(f(i+2^{j-1},j-1)\),那么它的最大值就是这两部分的最大值中较大的那一个。

于是有状态转移方程:

dp[i][j] = max(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1])

预处理代码

void init()
{
	for (int i = 1; i <= n; i++)
	{
		dp[i][0] = a[i];
	}
	for (int j = 1; (1 << j) <= n; j++)
	{
		for (int i = 1; i + (1 << j) - 1 <= n; i++)
		{
			dp[i][j] = max(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
		}
	}
}

2.查询

我们记 \(L\) 表示区间 \([l,r]\) 的长度,则 \(L=r-l+1\),又设 \(k=\log(L)\)

如果 \(L\) 不为 \(2\) 的次方,那么 \(2^k\) 就会 \(<L\),从而会有遗漏,因此答案不为 \(f(l,k)\)。但是 \(L<2^{k+1}\),所以只需要从 \(l\) 往右数 \(2^k\) 个,从 \(r\) 往左数 \(2^k\) 个即可,显然重复部分不影响结果。

于是有:

ans = max(dp[l][k], dp[r - (1 << k) + 1][k])

查询代码

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

3.模板题

P3865 【模板】ST表

AC Code

#include <iostream>
#include <cstdio>
#include <cmath>
#define int long long
using namespace std;

const int MAXN = 1e5 + 5;

int n, m;
int a[MAXN], dp[MAXN][20];

void init()
{
	for (int i = 1; i <= n; i++)
	{
		dp[i][0] = a[i];
	}
	for (int j = 1; (1 << j) <= n; j++)
	{
		for (int i = 1; i + (1 << j) - 1 <= n; i++)
		{
			dp[i][j] = max(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
		}
	}
}

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

signed main()
{
	scanf("%lld%lld", &n, &m);
	for (int i = 1; i <= n; i++)
	{
		scanf("%lld", &a[i]);
	}
	init();
	while (m--)
	{
		int x, y;
		scanf("%lld%lld", &x, &y);
		printf("%lld\n", query(x, y));
	}
	return 0;
}
posted @ 2021-08-07 17:52  mango09  阅读(96)  评论(0编辑  收藏  举报
-->