引入
RMQ \text{RMQ} RMQ 问题
RMQ \text{RMQ} RMQ 是英文 Range Maximum/Minimum Query \text{Range Maximum/Minimum Query} Range Maximum/Minimum Query 的缩写,表示区间最大(最小)值。一般可用 $\text{ST} $ 表、线段树等数据结构解决。
可重复贡献问题
对于一个运算 opt \text{opt} opt ,如果满足 x opt x = x x \text{ opt } x=x x opt x = x ,则对应的区间询问就是一个可重复贡献问题。例如最大值有 max ( x , x ) = x \max(x,x)=x max ( x , x ) = x ,最大公约数有 gcd ( x , x ) = x \gcd(x,x)=x g cd( x , x ) = x ,所以 RMQ \text{RMQ} RMQ 和区间 GCD \text{GCD} GCD 就是一个可重复贡献问题;而区间和就不具有这个性质,x + x = 2 x x+x=2x x + x = 2 x 。
ST \text{ST} ST 表
ST \text{ST} ST 表的特点
对于一个静态数组,给出 Q Q Q 次询问,每次询问区间 [ l , r ] [l,r] [ l , r ] 的最大值,最暴力的方法是每次询问都遍历区间 [ l , r ] [l,r] [ l , r ] ,时间复杂度最坏为 O ( Q n ) O(Qn) O ( Q n ) 。
对于 RMQ \text{RMQ} RMQ 问题,ST \text{ST} ST 表通过 $O(n\log n) $的预处理,使得 RMQ \text {RMQ} RMQ 问题的查询在 O ( 1 ) O(1) O ( 1 ) 内完成,但是这同时意味着ST \text{ST} ST 表有不可进行修改操作的缺点。
对于一个可重复贡献问题,opt \text{opt} opt 必须同时满足结合律才能使用 ST \text{ST} ST 表求解,这一点是和线段树类似的。
倍增思想
ST \text{ST} ST 表的预处理是通过倍增思想实现的。下面以 RMQ \text{RMQ} RMQ 为例阐述其实现过程。
给定一个数列$\{a_n\}$。定义 f [ i ] [ j ] f[i][j] f [ i ] [ j ] 表示区间 [ i , i + 2 j − 1 ] \left[i,i+2^j-1\right] [ i , i + 2 j − 1 ] 的最大值。
根据定义式,显然有 f [ i ] [ 0 ] = a [ i ] f[i][0]=a[i] f [ i ] [ 0 ] = a [ i ] ;类似树上倍增的思想,有递推式 f [ i ] [ j ] = max ( f [ i ] [ j − 1 ] , f [ i + 2 j − 1 ] [ j − 1 ] ) f[i][j]=\max(f[i][j-1],f[i+2^{j-1}][j-1]) f [ i ] [ j ] = max ( f [ i ] [ j − 1 ] , f [ i + 2 j − 1 ] [ j − 1 ]) 。
f [ i ] [ j − 1 ] f[i][j-1] f [ i ] [ j − 1 ] 表示区间 [ i , i + 2 j − 1 − 1 ] [i,i+2^{j-1}-1] [ i , i + 2 j − 1 − 1 ] 的最大值;f [ i + 2 j − 1 ] [ j − 1 ] f[i+2^{j-1}][j-1] f [ i + 2 j − 1 ] [ j − 1 ] 表示区间 [ i + 2 j − 1 , i + 2 j − 1 ] [i+2^{j-1},i+2^j-1] [ i + 2 j − 1 , i + 2 j − 1 ] 的最大值,两者覆盖整个区间 [ i , i + 2 j − 1 ] \left[i,i+2^j-1\right] [ i , i + 2 j − 1 ] 。
查询 RMQ \text{RMQ} RMQ
我们查询两个区间[ l , l + 2 s − 1 ] [l,l+2^s-1] [ l , l + 2 s − 1 ] ,[ r − 2 s + 1 , r ] [r-2^s+1,r] [ r − 2 s + 1 , r ] (即 f [ l ] [ s ] , f [ r − 2 s + 1 ] [ s ] f[l][s],f[r-2^s+1][s] f [ l ] [ s ] , f [ r − 2 s + 1 ] [ s ] ),其中 s = ⌊ log 2 ( r − l + 1 ) ⌋ s=\lfloor \log_2(r-l+1)\rfloor s = ⌊ log 2 ( r − l + 1 )⌋ 。两个区间的交集非空,两个区间的并集正是区间 [ l , r ] [l,r] [ l , r ] 。因此 [ l , r ] [l,r] [ l , r ] 区间的最大值为 max ( f [ l ] [ s ] , f [ r − 2 s + 1 ] [ s ] ) \max(f[l][s],f[r-2^s+1][s]) max ( f [ l ] [ s ] , f [ r − 2 s + 1 ] [ s ]) 。正是因为运算 max \max max 满足结合律和可重复贡献性,以上的分析是正确的。
题目描述
给定一个长度为 N N N 的数列,和 M M M 次询问,求出每一次询问的区间内数字的最大值。
输入格式
第一行包含两个整数 N N N , M M M 分别表示数列的长度和询问的个数。
第二行包含 N N N 个整数(记为 a i a_i a i ),依次表示数列的第 i i i 项。
接下来 M M M 行,每行包含两个整数 l i , r i l_i, r_i l i , r i ,表示查询的区间为 [ l i , r i ] [ l_i, r_i] [ l i , r i ] 。
输出格式
输出包含 M M M 行,每行一个整数,依次表示每一次询问的结果。
输入输出样例
输入 #1
8 8
9 3 1 7 5 6 0 8
1 6
1 5
2 7
2 6
1 8
4 8
3 7
1 8
输出 #1
9
9
7
7
9
8
7
9
说明/提示
对于 30 % 30\% 30% 的数据,满足: 1 ≤ N , M ≤ 10 1 \leq N, M \leq 10 1 ≤ N , M ≤ 10
对于 70 % 70\% 70% 的数据,满足:1 ≤ N , M ≤ 10 5 1 \leq N, M \leq {10}^5 1 ≤ N , M ≤ 10 5
对于 100 % 100\% 100% 的数据,满足:1 ≤ N ≤ 10 5 1 \leq N \leq {10}^5 1 ≤ N ≤ 10 5 , 1 ≤ M ≤ 2 × 10 6 1 \leq M \leq 2 \times {10}^6 1 ≤ M ≤ 2 × 10 6 , a i ∈ [ 0 , 10 9 ] a_i \in [0, {10}^9] a i ∈ [ 0 , 10 9 ] , 1 ≤ l i ≤ r i ≤ N 1 \leq l_i \leq r_i \leq N 1 ≤ l i ≤ r i ≤ N
代码
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 #include <cstdio> #include <algorithm> #define N 100002 using namespace std;int a[N];int _log2[N];int f[N][31 ];int n,m;void pre () { _log2[1 ]=0 ; int i; for (i=2 ;i<N;i++) _log2[i]=_log2[i>>1 ]+1 ; } void init () { int i,j; for (i=1 ;i<=n;i++) f[i][0 ]=a[i]; for (j=1 ;(1 <<j)<=n;j++) { for (i=1 ;i+(1 <<j)-1 <=n;i++) { f[i][j]=max (f[i][j-1 ],f[i+(1 <<j-1 )][j-1 ]); } } } int main () { scanf ("%d%d" ,&n,&m); int i; pre (); for (i=1 ;i<=n;i++) scanf ("%d" ,a+i); init (); while (m--) { int l,r; scanf ("%d%d" ,&l,&r); int s=_log2[r-l+1 ]; printf ("%d\n" ,max (f[l][s],f[r-(1 <<s)+1 ][s])); } return 0 ; }
二维 RMQ \text{RMQ} RMQ
给定一个 m × n m×n m × n 的矩阵,Q Q Q 次询问,每次询问以 ( r 1 , c 1 ) (r1,c1) ( r 1 , c 1 ) 为左上角,( r 2 , c 2 ) (r2,c2) ( r 2 , c 2 ) 为右下角的最大值。
问题分析
暴力方法遍历矩阵的时间复杂度为 O ( Q m n ) O(Qmn) O ( Q mn ) 。如果用 m m m 棵线段树或者树状数组来维护每行区间最大值,最坏时间复杂度为 O ( m n log n + Q m log n ) O(mn\log n+Qm\log n) O ( mn log n + Q m log n ) 。
既然是静态询问,我们可以用 二维 ST \text {ST} ST 表处理这样的二维 $\text{RMQ} $ 问题。
二维 ST \text{ST} ST 表
预处理
对于一个矩阵 a m n a_{mn} a mn ,定义 f [ i ] [ j ] [ k ] [ l ] f[i][j][k][l] f [ i ] [ j ] [ k ] [ l ] 为以坐标 ( i , j ) (i,j) ( i , j ) 为左上角,以坐标 ( i + 2 k − 1 , j + 2 l − 1 ) (i+2^k-1,j+2^l-1) ( i + 2 k − 1 , j + 2 l − 1 ) 为右下角的矩阵的最大值。预处理的时间复杂度为 O ( m n log m log n ) O(mn\log m\log n) O ( mn log m log n ) 。
对于一块以坐标 ( i , j ) (i,j) ( i , j ) 为左上角,以坐标 ( i + 2 k − 1 , j + 2 l − 1 ) (i+2^k-1,j+2^l-1) ( i + 2 k − 1 , j + 2 l − 1 ) 为右下角的区域,如上图横着劈成两半,得到两块区域:以坐标 ( i , j ) (i,j) ( i , j ) 为左上角 ,以坐标 ( i + 2 k − 1 , j + 2 l − 1 ) (i+2^k-1,j+2^{l-1}) ( i + 2 k − 1 , j + 2 l − 1 ) 为右下角的上半部分;以坐标 $ (i,j+2^{l-1})$ 为左上角,以坐标 ( i + 2 k − 1 , j + 2 l − 1 ) (i+2^k-1,j+2^l-1) ( i + 2 k − 1 , j + 2 l − 1 ) 为右下角的下半部分。将两部分的最大值合并即可得到整个区域的最大值。
下面直接给出状态转移方程
f [ i ] [ j ] [ k ] [ l ] = max ( f [ i ] [ j ] [ k ] [ l − 1 ] , f [ i ] [ j + 2 l − 1 ] [ k ] [ l − 1 ] ) f[i][j][k][l]=\max\left(f[i][j][k][l-1],f[i][j+2^{l-1}][k][l-1]\right) f [ i ] [ j ] [ k ] [ l ] = max ( f [ i ] [ j ] [ k ] [ l − 1 ] , f [ i ] [ j + 2 l − 1 ] [ k ] [ l − 1 ] )
f [ i ] [ j ] [ k ] [ l ] = max ( f [ i ] [ j ] [ k − 1 ] [ l ] , f [ i + 2 k − 1 ] [ j ] [ k − 1 ] [ l ] ) f[i][j][k][l]=\max\left(f[i][j][k-1][l],f[i+2^{k-1}][j][k-1][l]\right) f [ i ] [ j ] [ k ] [ l ] = max ( f [ i ] [ j ] [ k − 1 ] [ l ] , f [ i + 2 k − 1 ] [ j ] [ k − 1 ] [ l ] )
查询
对于一个子矩阵,设左上角坐标为 ( r 1 , c 1 ) (r_1,c_1) ( r 1 , c 1 ) ,右下角为 ( r 2 , c 2 ) (r_2,c_2) ( r 2 , c 2 ) ,可以通过预处理的二维 ST \text{ST} ST 表,将其分成四部分查询。
四个部分分别为图中 W 1 ( A , E , F , G ) , W 2 ( H , B , J , I ) , W 3 ( M , L , K , C ) , W 4 ( O , P , D , N ) W_1(A,E,F,G),W_2(H,B,J,I),W_3(M,L,K,C),W_4(O,P,D,N) W 1 ( A , E , F , G ) , W 2 ( H , B , J , I ) , W 3 ( M , L , K , C ) , W 4 ( O , P , D , N ) 。
令 p = ⌊ log ( r 2 − r 1 + 1 ) ⌋ , q = ⌊ log ( c 2 − c 1 + 1 ) ⌋ p=\lfloor \log(r_2−r_1+1)\rfloor,q=\lfloor\log(c_2−c_1+1)\rfloor p = ⌊ log ( r 2 − r 1 + 1 )⌋ , q = ⌊ log ( c 2 − c 1 + 1 )⌋ ,则:
${ans=\max\displaystyle {\begin{cases}f[r_1][c_1][p][q]&W_1\\f[r_2-2^p+1][c_1][p][q]&W_2\\f[r_1][c_2-2^q+1][p][q]&W_3\\f[r_2-2^p+1][c_2-2^q+1][p][q]&W_4\end{cases}}}$
Problem Description
Paul draw a big m × n m\times n m × n matrix A A A last month, whose entries $A_ {i j } $ are all integer numbers ( 1 ≤ i ≤ m , 1 ≤ j ≤ n ) ( 1\le i \le m, 1\le j\le n ) ( 1 ≤ i ≤ m , 1 ≤ j ≤ n ) . Now he selects some sub-matrices, hoping to find the maximum number. Then he finds that there may be more than one maximum number, he also wants to know the number of them. But soon he find that it is too complex, so he changes his mind, he just want to know whether there is a maximum at the four corners of the sub-matrix, he calls this “Check corners”. It’s a boring job when selecting too many sub-matrices, so he asks you for help. (For the “Check corners” part: If the sub-matrix has only one row or column just check the two endpoints. If the sub-matrix has only one entry just output “yes”.)
There are multiple test cases.
For each test case, the first line contains two integers m , n m, n m , n ( 1 ≤ m , n ≤ 300 ) (1 \le m, n \le 300) ( 1 ≤ m , n ≤ 300 ) , which is the size of the row and column of the matrix, respectively. The next m m m lines with n n n integers each gives the elements of the matrix which fit in non-negative 32 32 32 -bit integer.
The next line contains a single integer Q Q Q ( 1 ≤ Q ≤ 1000000 ) (1\le Q \le 1000000) ( 1 ≤ Q ≤ 1000000 ) , the number of queries. The next Q Q Q lines give one query on each line, with four integers r 1 , c 1 , r 2 , c 2 r_1, c_1, r_2, c_2 r 1 , c 1 , r 2 , c 2 ( 1 ≤ (1 \le ( 1 ≤ r 1 r_1 r 1 ≤ \le ≤ r 2 r_2 r 2 ≤ m \le m ≤ m , 1 1 1 ≤ \le ≤ c 1 c_1 c 1 ≤ \le ≤ c 2 c_2 c 2 ≤ \le ≤ $ n)$, which are the indices of the upper-left corner and lower-right corner of the sub-matrix in question.
Output
For each test case, print Q Q Q lines with two numbers on each line, the required maximum integer and the result of the “Check corners” using “yes” or “no”. Separate the two parts with a single space.
4 4
4 4 10 7
2 13 9 11
5 7 8 20
13 20 8 2
4
1 1 4 4
1 1 3 3
1 3 3 4
1 1 1 1
Sample Output
20 no
13 no
20 yes
4 yes
Translation
给定一个 m × n m×n m × n 的矩阵,Q Q Q 次询问,每次询问以 ( r 1 , c 1 ) (r_1,c_1) ( r 1 , c 1 ) 为左上角,( r 2 , c 2 ) (r_2,c_2) ( r 2 , c 2 ) 为右下角的区域的最大值,并判断最大值是否在边界的四个点上取到。
Code
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 #include <algorithm> #include <cstdio> #define N 305 using namespace std;int m,n,q;int f[N][N][9 ][9 ];int _log2[N],a[N][N];void pre () { int i; _log2[1 ]=0 ; for (i=2 ;i<N;i++) _log2[i]=_log2[i>>1 ]+1 ; } void init () { int i,j; for (i=1 ;i<=m;i++) { for (j=1 ;j<=n;j++) { f[i][j][0 ][0 ]=a[i][j]; } } int l,k; for (k=0 ;(1 <<k)<=m;k++) { for (l=0 ;(1 <<l)<=n;l++) { if (!k&&!l) continue ; for (i=1 ;i+(1 <<k)-1 <=m;i++) { for (j=1 ;j+(1 <<l)-1 <=n;j++) { if (l) f[i][j][k][l]=max (f[i][j][k][l-1 ],f[i][j+(1 <<l-1 )][k][l-1 ]); else f[i][j][k][l]=max (f[i][j][k-1 ][l],f[i+(1 <<k-1 )][j][k-1 ][l]); } } } } } int query (int x1,int y1,int x2,int y2) { int p=_log2[x2-x1+1 ]; int q=_log2[y2-y1+1 ]; return max ({f[x1][y1][p][q],f[x1][y2-(1 <<q)+1 ][p][q],f[x2-(1 <<p)+1 ][y1][p][q],f[x2-(1 <<p)+1 ][y2-(1 <<q)+1 ][p][q]}); } int main () { pre (); while (~scanf ("%d%d" ,&m,&n)) { int i,j; for (i=1 ;i<=m;i++) { for (j=1 ;j<=n;j++) { scanf ("%d" ,&a[i][j]); } } init (); int q; scanf ("%d" ,&q); while (q--) { int r1,c1,r2,c2; scanf ("%d%d%d%d" ,&r1,&c1,&r2,&c2); int ans=query (r1,c1,r2,c2); printf ("%d " ,ans); if (ans==a[r1][c1]||ans==a[r1][c2]||ans==a[r2][c2]||ans==a[r2][c1]) puts ("yes" ); else puts ("no" ); } } return 0 ; }