上一篇是線段樹的做法,這一個是用RMQ方法做的,順便學習一下RMQ。
說是RMQ實際上就是dp的變形。
RMQ定義:
RMQ (Range Minimum/Maximum Query)問題是指:對於長度為n的數列A,回答若干詢問RMQ(A,i,j)(i,j<=n),返回數列A中下標在i,j裡的最小(大)值,也就是說,RMQ問題是指求區間最值的問題。
主要方法及複雜度如下:
1、樸素(即搜尋),O(n)-O(qn) online。
2、線段樹,O(n)-O(qlogn) online。
3、ST(實質是動態規劃),O(nlogn)-O(1) online。
ST演算法(Sparse Table),以求最大值為例,設d[i,j]表示[i,i+2^j-1]這個區間內的最大值,那麼在詢問到[a,b]區間的最大值時答案就是max(d[a,k], d[b-2^k+1,k]),其中k是滿足2^k<=b-a+1(即長度)的最大的k,即k=[ln(b-a+1)/ln(2)]。
d的求法可以用動態規劃,d[i, j]=max(d[i, j-1],d[i+2^(j-1), j-1])。
4、RMQ標準演算法:先規約成LCA(Lowest Common Ancestor),再規約成約束RMQ,O(n)-O(1) online。
首先根據原數列,建立笛卡爾樹,從而將問題線上性時間內規約為LCA問題。LCA問題可以線上性時間內規約為約束RMQ,也就是數列中任意兩個相鄰的數的差都是+1或-1的RMQ問題。約束RMQ有O(n)-O(1)的線上解法,故整個演算法的時間複雜度為O(n)-O(1)。
這裡的狀態方程dp[j][i] = max(dp[j][i - 1], dp[j + (1 <<(i - 1))][i - 1]);
dp[j][i]標示從j開始前2^i個數中最大重複次數。
有了這些,其他和上一篇基本上一樣的,所以代碼極其相似。
//RMQ#include <iostream>#include <cmath>using namespace std;int arr[100010],hash[100010];struct Part{int s, e, cnt;}part[100010];int dp[100010][18];//dp[i][j] is for : the max value rang from i to (i + 2^j - 1)int Max(int a, int b){return a > b ? a : b;}void Dp(int n){int i, j, tmp;for (i = 1; i <= n; ++ i){dp[i][0] = part[i].cnt;}tmp = log(n + 1.0)/log(2.0);for (i = 1; i <= tmp; ++ i){for (j = 1; j + (1 << i) - 1 <= n ; ++ j){dp[j][i] = Max(dp[j][i - 1], dp[j + (1 << (i - 1))][i - 1]);}}}int getMax(int a, int b){int k = (int )((log(b - a + 1.0)) / log(2.0));return Max(dp[a][k], dp[b - (1 << k) + 1][k]);}int main(){int n, q;while (scanf("%d", & n) && n){scanf("%d", & q);for (int i = 1; i <= n; ++ i){scanf("%d", & arr[i]);}memset(part, 0, sizeof(part));int id = 1;part[1].s = id;for (int i = 1; i <= n; ++ i){part[id].cnt ++;hash[i] = id;if (arr[i] != arr[i + 1] || i == n){part[id].e = i;++ id;part[id].s = i + 1;}}Dp(id - 1);int a, b;while (q --){scanf("%d %d", & a, & b);if (hash[a] == hash[b])//the same part{printf("%d\n", b - a + 1);}else// two situations{int n1 = part[hash[a]].e - a + 1;int n2 = b - part[hash[b]].s + 1;int n3 = 0;if (hash[b] - hash[a] > 1){n3 = getMax(hash[a] + 1, hash[b] - 1);}printf("%d\n", Max(Max(n1, n2),n3));}}}return 0;}