Improved RMQ

因为有点无聊,所以打了一下这个算法,发现还可以。卡常必备。

原问题

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

经典做法

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

改进方法

我们把一开始的 N 个数分块。每一块的大小为logN.然后O(N)扫得每一块的最小值 Mi .然后对每一块都做一遍ST算法。再对 Mi 做一遍ST算法。那么预处理的时间复杂度就是 O(NlogNlogN+NlogNlogNlog(logN))=O(N+Nlog(logN)) .
接着对于一个询问,假设为 l,r .设 Blocki 表示位置 i 所属的块。那么我们可以把一个询问分为三段。第一段为 Blockl l 到结尾。第二段为 Blockl+1Blockr1 .第三段为 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;
}


你可能感兴趣的:(Improved RMQ)