上一篇是线段树的做法,这一个是用RMQ方法做的,顺便学习一下RMQ。
说是RMQ实际上就是dp的变形。
RMQ定义:
RMQ (Range Minimum/Maximum Query)问题是指:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j里的最小(大)值,也就是说,RMQ问题是指求区间最值的问题。
主要方法及复杂度如下:
1、朴素(即搜索),O(n)-O(qn) online。
2、线段树,O(n)-O(qlogn) online。
3、ST(实质是动态规划),O(nlogn)-O(1) online。
ST算法(Sparse Table),以求最大值为例,设d[i,j]表示[i,i+2^j-1]这个区间内的最大值,那么在询问到[a,b]区间的最大值时答案就是max(d[a,k], d[b-2^k+1,k]),其中k是满足2^k<=b-a+1(即长度)的最大的k,即k=[ln(b-a+1)/ln(2)]。
d的求法可以用动态规划,d[i, j]=max(d[i, j-1],d[i+2^(j-1), j-1])。
4、RMQ标准算法:先规约成LCA(Lowest Common Ancestor),再规约成约束RMQ,O(n)-O(1) online。
首先根据原数列,建立笛卡尔树,从而将问题在线性时间内规约为LCA问题。LCA问题可以在线性时间内规约为约束RMQ,也就是数列中任意两个相邻的数的差都是+1或-1的RMQ问题。约束RMQ有O(n)-O(1)的在线解法,故整个算法的时间复杂度为O(n)-O(1)。
这里的状态方程dp[j][i] = max(dp[j][i - 1], dp[j + (1 <<(i - 1))][i - 1]);
dp[j][i]标示从j开始前2^i个数中最大重复次数。
有了这些,其他和上一篇基本上一样的,所以代码极其相似。
//RMQ
#include <iostream>
#include <cmath>
using namespace std;
int arr[100010],hash[100010];
struct Part
{
int s, e, cnt;
}part[100010];
int dp[100010][18];//dp[i][j] is for : the max value rang from i to (i + 2^j - 1)
int Max(int a, int b)
{
return a > b ? a : b;
}
void Dp(int n)
{
int i, j, tmp;
for (i = 1; i <= n; ++ i)
{
dp[i][0] = part[i].cnt;
}
tmp = log(n + 1.0)/log(2.0);
for (i = 1; i <= tmp; ++ i)
{
for (j = 1; j + (1 << i) - 1 <= n ; ++ j)
{
dp[j][i] = Max(dp[j][i - 1], dp[j + (1 << (i - 1))][i - 1]);
}
}
}
int getMax(int a, int b)
{
int k = (int )((log(b - a + 1.0)) / log(2.0));
return Max(dp[a][k], dp[b - (1 << k) + 1][k]);
}
int main()
{
int n, q;
while (scanf("%d", & n) && n)
{
scanf("%d", & q);
for (int i = 1; i <= n; ++ i)
{
scanf("%d", & arr[i]);
}
memset(part, 0, sizeof(part));
int id = 1;
part[1].s = id;
for (int i = 1; i <= n; ++ i)
{
part[id].cnt ++;
hash[i] = id;
if (arr[i] != arr[i + 1] || i == n)
{
part[id].e = i;
++ id;
part[id].s = i + 1;
}
}
Dp(id - 1);
int a, b;
while (q --)
{
scanf("%d %d", & a, & b);
if (hash[a] == hash[b])//the same part
{
printf("%d\n", b - a + 1);
}
else// two situations
{
int n1 = part[hash[a]].e - a + 1;
int n2 = b - part[hash[b]].s + 1;
int n3 = 0;
if (hash[b] - hash[a] > 1)
{
n3 = getMax(hash[a] + 1, hash[b] - 1);
}
printf("%d\n", Max(Max(n1, n2),n3));
}
}
}
return 0;
}