ST表是一种区间查找最大值的数据结构,它虽说有的短板——不支持修改,但它的特点比较牛逼,能做到O(nlogn)的预处理,O(1)的单次查询,其思路其实就是我们所熟悉的倍增思想。
基本思路:
我们现在用O(1)的复杂度求区间最大值,那么会有一个简单的思路,令f(i,j),为区间[i,j]的最大值,但这样的话预处理复杂度达到O(n^2),复杂度太大。考虑优化,有一个关键性质:max操作允许区间重叠,也就是max(a,b,c)=max(max(a,b),max(b,c))
这里我们采用倍增思想,令f(i,j)为a[i]开始的数,表示第i号节点,到第i+2^j-1号节点,连续j ^ 2个的区间的最大值。
f(i,j) = max(f(i,j-1),f(i+2^(j-1),j-1))
初始化时,每一个状态对应的区间长度都为2^ j,由于给出的查询区间长度不一定恰好为2^ j。
现在还需要引出一个定理:2^log(x)>x/2
,任意一个长度为x的区间,都能被两端长度为2^log(x)的区间所覆盖。
所以我们的查询也变得很简单了,假如我们要查询区间[x,y]的最大值,很显然,这段区间长度t=y-x+1;那么我们只要求出以x为首,长度为2 ^ log(t)的区间的最大值 和 以y为尾,长度为2 ^log(t)的区间的最大值,就一定包括了整个区间的最大值。
max(f(x,log t),f(y-2^log t+1,log t))
代码实现:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<stdio.h>
using namespace std;
long f[100010][30] = { 0 }; //st表
long a[100010];//输入数组
int main()
{
long int n;
//cin >> n;
long int m;
//cin >> m;
scanf("%ld %ld", &n, &m);
for (long i = 1; i <= n; i++)
{
long c;
scanf("%ld", &c);
f[i][0] = c;
//cin >> a[i];
//f[i][0] = a[i];//实际上可直接输入到ST表内
}
for (int j = 1; j <= log2(n) + 1; j++)
{
for (long int i = 1; i + (1 << j)-1 <= n; i++)
{
f[i][j] = max(f[i][j-1], f[i+(1<<(j-1))][j-1]);
//1<<(j-1)相当于2^(j-1),详情查看位运算
}
}
//cin >> m;//查询次数
for (long int i = 1; i <= m; i++)
{
long int x, y;//查找区间[x,y]中最大的数字
scanf("%ld %ld", &x, &y);
long int t = y - x + 1;
int lg = (int)log2(t);
printf("%ld\n",max(f[x][lg], f[y-(1<<lg)+1][lg]));
//cout << max(f[x][lg], f[y-(1<<lg)+1][lg]) << endl;
}
}
提示:cin,cout,要比printf,scanf慢,所以在卡时间的情况下使用后者!