因为有点无聊,所以打了一下这个算法,发现还可以。卡常必备。
给定 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[n];++ 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[N];
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;
}