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;
        }
    }
};
posted @ 2015-09-02 00:38 jklongint 阅读(...) 评论(...) 编辑 收藏