RMQ(Range Minimum/Maximum Query)问题是区间最值问题。如果用线段树,预处理和查询的复杂度都是O(logn)。而ST算法,可以做到O(nlogn)的预处理,O(1)地回答每个询问
ST算法实现(以最大值为例): 首先是预处理,用DP解决。设a[i]是要求区间最值的数列,f[i,j]表示从第i个数起连续2^j个数中的最大值。可以看出f[i,0]其实就等于a[i]。这样,Dp的状态、初值都已经有了,剩下的就是状态转移方程。我们把f[i,j]平均分成两段(因为f[i,j]一定是偶数个数字中的),从i到i+2^(j-1)-1为一段,i+2^(j-1)到i+2^j-1为一段(长度都为2^(j-1))。于是我们得到了动规方程F[i,j]=max(F[i,j-1],F[i+2^(j-i),j-1])。
接下来求最值的时候,就是把区间[l,r]分成两个长度为2^n的区间,近似于二分。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#define eps 1e-5
#define MAXN 55555
#define MAXM 11111
#define INF 1000000000
#define lch(x) x<<1
#define rch(x) x<<1|1
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
int mi[MAXN][17], mx[MAXN][17], w[MAXN];
int n, q;
void rmqinit()
{
for(int i = 1; i <= n; i++) mi[i][0] = mx[i][0] = w[i];
int m = (int)(log(n * 1.0) / log(2.0));
for(int i = 1; i <= m; i++)
for(int j = 1; j <= n; j++)
{
mx[j][i] = mx[j][i - 1];
if(j + (1 << (i - 1)) <= n) mx[j][i] = max(mx[j][i], mx[j + (1 << (i - 1))][i - 1]);
mi[j][i] = mi[j][i - 1];
if(j + (1 << (i - 1)) <= n) mi[j][i] = min(mi[j][i], mi[j + (1 << (i - 1))][i - 1]);
}
}
int rmqmin(int l,int r)
{
int m = (int)(log((r - l + 1) * 1.0) / log(2.0));
return min(mi[l][m] , mi[r - (1 << m) + 1][m]);
}
int rmqmax(int l,int r)
{
int m = (int)(log((r - l + 1) * 1.0) / log(2.0));
return max(mx[l][m] , mx[r - (1 << m) + 1][m]);
}
int main()
{
scanf("%d%d", &n, &q);
for(int i = 1; i <= n; i++) scanf("%d", &w[i]);
rmqinit();
int l, r;
while(q--)
{
scanf("%d%d", &l, &r);
printf("%d\n", rmqmax(l, r));
}
return 0;
}
下面是返回下标的代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#define eps 1e-5
#define MAXN 55555
#define MAXM 11111
#define INF 1000000000
#define lch(x) x<<1
#define rch(x) x<<1|1
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
int mi[MAXN][17], mx[MAXN][17], w[MAXN];
int n, q;
void rmqinit()
{
for(int i = 1; i <= n; i++) mi[i][0] = mx[i][0] = i;
int m = (int)(log(n * 1.0) / log(2.0));
for(int i = 1; i <= m; i++)
for(int j = 1; j <= n; j++)
{
mx[j][i] = mx[j][i - 1];
mi[j][i] = mi[j][i - 1];
if(j + (1 << (i - 1)) <= n)
{
if(w[mx[j][i]] < w[mx[j + (1 << (i - 1))][i - 1]]) mx[j][i] = mx[j + (1 << (i - 1))][i - 1];
if(w[mi[j][i]] > w[mi[j + (1 << (i - 1))][i - 1]]) mi[j][i] = mi[j + (1 << (i - 1))][i - 1];
}
}
}
int rmqmin(int l,int r)
{
int m = (int)(log((r - l + 1) * 1.0) / log(2.0));
if(w[mi[l][m]] > w[mi[r - (1 << m) + 1][m]]) return mi[r - (1 << m) + 1][m];
else return mi[l][m];
}
int rmqmax(int l,int r)
{
int m = (int)(log((r - l + 1) * 1.0) / log(2.0));
if(w[mx[l][m]] < w[mx[r - (1 << m) + 1][m]]) return mx[r - (1 << m) + 1][m];
else return mx[l][m];
}
int main()
{
scanf("%d%d", &n, &q);
for(int i = 1; i <= n; i++) scanf("%d", &w[i]);
rmqinit();
int l, r;
while(q--)
{
scanf("%d%d", &l, &r);
printf("%d\n", rmqmax(l, r));
}
return 0;
}