您的位置:首页 > 其它

Improved RMQ

2015-10-01 18:03 295 查看
因为有点无聊,所以打了一下这个算法,发现还可以。卡常必备。

原问题

给定N个数Ai.有Q个询问,每个询问询问一段区间的最小(大)值。

经典做法

RMQ问题的经典做法是ST表。即先预处理出Fi,j,表示以i为起点,长度为2j的一段的最小值是多少。然后在询问的时候直接把区间拆成两段可能重复的区间来取个min。这样预处理的时间复杂度就是O(NlogN).单个询问O(1)。

改进方法

我们把一开始的N个数分块。每一块的大小为logN.然后O(N)扫得每一块的最小值Mi.然后对每一块都做一遍ST算法。再对Mi做一遍ST算法。那么预处理的时间复杂度就是O(NlogN∗logN+NlogN∗logN∗log(logN))=O(N+Nlog(logN)).

接着对于一个询问,假设为l,r.设Blocki表示位置i所属的块。那么我们可以把一个询问分为三段。第一段为Blockl中l到结尾。第二段为Blockl+1→Blockr−1.第三段为Blockr中起点到r.

那么因为对于已经处理出ST表的地方可以O(1)完成询问。所以我们的询问也是O(1)的。

那么总的时间复杂度就是O(N+Nlog(logN)+Q)了。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define max(a,b) (a > b ? a : b)

using namespace std;

const int MAXN = 10000005,LG = 25;

int Log[MAXN],V[MAXN],F[LG][MAXN / 23 + 2],Ti[LG],N,Len;

struct BLOCK
{
int F[5][LG],Back[LG],Front[LG],n;

void Pre_Treat()
{
Front[0] = F[0][0];
for(int i = 1;i < n;++ i) Front[i] = max(Front[i - 1],F[0][i]);
for(int i = n - 1;i + 1;-- i) Back[i] = max(Back[i + 1],F[0][i]);
for(int i = 1,q = 2;i <= Log
;++ i,q <<= 1)
for(int j = 0;j + q <= n;++ j)
F[i][j] = max(F[i - 1][j],F[i - 1][j + (q >> 1)]);
}

int Query(int l,int r)
{
int p = Log[r - l + 1];
return max(F[p][l],F[p][r - Ti[p] + 1]);
}
}Block[MAXN / 23 + 2];

void read(int &x)
{
char c;
while (c = getchar(),c < '0' || c > '9');
x = c - 48;
while (c = getchar(),c >= '0' && c <= '9') x = x * 10 + c - 48;
}

int Query(int l,int r)
{
if (l > r) return 0;
int p = Log[r - l + 1];
return max(F[p][l],F[p][r - Ti[p] + 1]);
}

int main()
{
read(N);
for(int i = 0;i < N;i ++) read(V[i]);
for(int i = 0,j = 1;i < LG;i ++,j <<= 1) Ti[i] = j;
for(int i = 0,j = 1,k = 1;k <= N;k ++)
if (k == j) Log[k] = i,i ++,j <<= 1; else
Log[k] = i - 1;
Len = Log
;
BLOCK *a = Block;
for(int i = 0,cur = 0;i < N;++ cur)
{
int lst = i,mx = 0;
for(int c = Len;c && i < N;-- c,++ i)
a->F[0][Len - c] = V[i],mx = max(V[i],mx);
F[0][cur] = mx;
a->n = i - lst + 1;
a->Pre_Treat();
++ a;
}
int all = (N - 1) / Len;
for(int i = 1,q = 2;i <= Log[all];++ i,q <<= 1)
for(int j = 0;j + q <= all;++ j)
F[i][j] = max(F[i - 1][j],F[i - 1][j + (q >> 1)]);
int M;
read(M);
for(int i = 1;i <= M;i ++)
{
int l,r;
read(l),read(r);
int b1 = l / Len,b2 = r / Len,p1 = l % Len,p2 = r % Len;
if (b1 == b2) printf("%d\n", Block[b1].Query(p1,p2)); else
{
int a = Query(b1 + 1,b2 - 1),b = Block[b1].Back[p1],c = Block[b2].Front[p2];
b = max(b,c);
printf("%d\n", max(a,b));
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: