RMQ問題總結,標准RMQ算法的實現


RMQ問題:對於長度為N的序列,詢問區間[L,R]中的最值

RMQ問題的幾種解法:

  1. 普通遍歷查詢,O(1)-O(N)
  2. 線段樹,O(N)-O(logN)
  3. DP,O(NlogN)-O(1)
  4. RMQ標准算法,O(N)-O(1)

簡單介紹:

  1. 朴素的查詢,不需要任何預處理,但結果是沒有任何已知的信息可以利用,每次都需要從頭遍歷到尾。
  2. 線段樹,區間問題的神器,用線段樹做比起朴素的暴力查詢要快得多,關鍵在於線段樹使用了分治思想,利用了區間問題的可合並性。任何一個區間最多只需要logN個線段樹上的區間來合並,線段樹上的區間總數目為O(N)個,因此只需要O(N)的預處理就可以將查詢復雜度降到O(logN)。同時線段樹的樹狀結構使得修改時信息更容易維護。
  3. DP,又叫ST算法,也是利用了分治的思想。任何一個區間都可以由兩個小於當前區間長度的最大的長度為2的冪的區間合並而來,於是預處理出每個點開始所有長度為2的冪的區間最值,那么查詢時就可以由預處理的信息O(1)得到答案。
  4. RMQ標准算法,利用了神奇的數據結構--笛卡爾樹,笛卡爾樹將區間最值問題轉化為樹上兩個點的LCA問題,而DFS可以將LCA問題轉化為±1RMQ問題,±1RMQ問題又可以利用分塊和動態規划的思想來解決。上述所有預處理,包括笛卡爾樹的建立、DFS序以及±1RMQ的問題的求解都可以在線性時間內完成,查詢時復雜度為O(1)。

標准算法的實現:

  • 結構圖:
  • 笛卡爾樹的構造算方法:從左至右掃描原序列,並依次插入到笛卡爾樹的右鏈中,使用單調棧復雜度為O(N)。建好樹后,key是二查搜索樹,value是小根堆。
  • 最小值與LCA:建好樹后,區間最小值問題便轉化為了LCA問題,下面簡單證明一下:

假設現在詢問[d, f]的最小值,root為d和f的LCA,由笛卡爾樹的性質可知,root是整棵樹表示區間的最小值,而[d, f]是其子區間,所以root不可能比[d, f]中的數小,又因為d和f屬於root的不同子樹(LCA的性質),所以root一定在[d, f]中(笛卡爾樹的性質),故對兩個點a,b,LCA(a, b)就是[a, b]的最小值,證畢。

  • ±1RMQ問題:相鄰兩個數相差1或者-1的序列的RMQ問題
  • ±1RMQ問題解法:將原長度為N的序列分成2N/logN塊,每塊長度為logN/2,將原來的詢問分解為塊間詢問和塊內詢問。用ST算法在O(N/logN*log(N/logN))=O(N)的時間內處理出塊與塊之間的區間最值信息,可以在O(1)的時間內解決塊與塊之間的詢問。對於塊內的詢問,由於每塊長度為logN/2,相鄰兩個數的差不是1就是-1,於是對於區間最值出現的位置,本質不同的狀態只有2logN/2=√N個,加上邊界,總共狀態數為O(√N*logNlogN),利用遞推在O(√N*logNlogN)的時間內求出所有狀態來,以后可以在O(1)的時間內得到塊內任意區間最值的位置。總復雜度為O(N + √N*logNlogN) ≈ O(N)。
  • LCA與±1RMQ的經典轉化就不細說了,詳見代碼

 

標准RMQ,O(N)-O(1)

 

 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
struct PlusMinusOneRMQ {
    const static int N = 223456;
    const static int M = 15;
    int blocklen, block, minv[N], dp[N][20],
        t[N * 20], f[1 << M][M][M], s[N];
    void init(int n) {
        blocklen = max(1, (int)(log(n * 1.0) / log(2.0)) / 2);
        block = n / blocklen + (n % blocklen > 0);
        int total = 1 << (blocklen - 1);
        for (int i = 0; i < total; i ++) {
            for (int l = 0; l < blocklen; l ++) {
                f[i][l][l] = l;
                int now = 0, minv = 0;
                for (int r = l + 1; r < blocklen; r ++) {
                    f[i][l][r] = f[i][l][r - 1];
                    if ((1 << (r - 1)) & i) now ++;
                    else {
                        now --;
                        if (now < minv) {
                            minv = now;
                            f[i][l][r] = r;
                        }
                    }
                }
            }
        }

        int tot = N * 20;
        t[1] = 0;
        for (int i = 2; i < tot; i ++) {
            t[i] = t[i - 1];
            if (!(i & (i - 1))) t[i] ++;
        }
    }
    void initmin(int a[], int n) {
        for (int i = 0; i < n; i ++) {
            if (i % blocklen == 0) {
                minv[i / blocklen] = i;
                s[i / blocklen] = 0;
            }
            else {
                if (a[i] < a[minv[i / blocklen]]) minv[i / blocklen] = i;
                if (a[i] > a[i - 1]) s[i / blocklen] |= 1 << (i % blocklen - 1);
            }
        }
        for (int i = 0; i < block; i ++) dp[i][0] = minv[i];
        for (int j = 1; (1 << j) <= block; j ++) {
            for (int i = 0; i + (1 << j) - 1 < block; i ++) {
                int b1 = dp[i][j - 1], b2 = dp[i + (1 << (j - 1))][j - 1];
                dp[i][j] = a[b1] < a[b2]? b1 : b2;
            }
        }
    }
    int querymin(int a[], int L, int R) {
        int idl = L / blocklen, idr = R / blocklen;
        if (idl == idr)
            return idl * blocklen + f[s[idl]][L % blocklen][R % blocklen];
        else {
            int b1 = idl * blocklen + f[s[idl]][L % blocklen][blocklen - 1];
            int b2 = idr * blocklen + f[s[idr]][0][R % blocklen];
            int buf = a[b1] < a[b2]? b1 : b2;
            int c = t[idr - idl - 1];
            if (idr - idl - 1) {

                int b1 = dp[idl + 1][c];
                int b2 = dp[idr - 1 - (1 << c) + 1][c];
                int b = a[b1] < a[b2]? b1 : b2;
                return a[buf] < a[b]? buf : b;
            }
            return buf;
        }
    }
};

struct CartesianTree {
private:
    struct Node {
        int key, value, l, r;
        Node(int key, int value) {
            this->key = key;
            this->value = value;
            l = r = 0;
        }
        Node() {}
    };
    Node tree[maxn];
    int sz;
    int S[maxn], top;
    /** 小根堆 區間最小值*/
public:
    void build(int a[], int n) {
        top = 0;
        tree[0] = Node(-1, 0x80000000);//將根的key和value賦為最小以保持樹根不變
        S[top ++] = 0;
        sz = 0;
        for (int i = 0; i < n; i ++) {
            tree[++ sz] = Node(i, a[i]);
            int last = 0;
            while (tree[S[top - 1]].value >= tree[sz].value) {
                last = S[top - 1];
                top --;
            }
            tree[sz].l = last;
            tree[S[top - 1]].r = sz;
            S[top ++] = sz;
        }
    }
    Node &operator [] (const int x) {
        return tree[x];
    }
};/** 樹根為定值0,數組從0開始編號 **/



class stdRMQ {
public:
    void work(int a[], int n) {
        ct.build(a, n);
        dfs_clock = 0;
        dfs(0, 0);
        rmq.init(dfs_clock);
        rmq.initmin(depseq, dfs_clock);
    }
    int query(int L, int R) {
        int cl = clk[L], cr = clk[R];
        if (cl > cr) swap(cl, cr);
        return value[rmq.querymin(depseq, cl, cr)];
    }
private:
    CartesianTree ct;
    PlusMinusOneRMQ rmq;
    int dfs_clock, clk[maxn], value[maxn << 1], depseq[maxn << 1];
    void dfs(int rt, int d) {
        clk[ct[rt].key] = dfs_clock;
        depseq[dfs_clock] = d;
        value[dfs_clock ++] = ct[rt].value;
        if (ct[rt].l) {
            dfs(ct[rt].l, d + 1);
            depseq[dfs_clock] = d;
            value[dfs_clock ++] = ct[rt].value;
        }
        if (ct[rt].r) {
            dfs(ct[rt].r, d + 1);
            depseq[dfs_clock] = d;
            value[dfs_clock ++] = ct[rt].value;
        }
    }
};


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM