二维单调队列
单调队列通常用来解决区间最值问题。
二维单调队列用来在矩阵中找子矩阵的最值,即求a * b大小的子矩阵中的最大值与最小值。
做法: 我们先预处理出每行滑动窗口长度为b的最值,并将其放到窗口最右侧位置;如0~b - 1窗口的最值放到 下标为b - 1 的位置。
处理完行后,我们对列进行处理,维护长度为a的滑动窗口,并将最值放到最下侧。
操作完后的子数组最值就在最右下角处。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 1010; int n, m, k; int w[N][N]; int row_min[N][N], row_max[N][N]; int q[N]; void get_min(int a[], int b[], int tot) { int hh = 0, tt = -1; for(int i = 1; i <= tot; i ++ ) { while(hh <= tt && q[hh] < i - k + 1) hh ++ ; while(hh <= tt && a[q[tt]] >= a[i]) tt -- ; q[ ++ tt] = i; b[i] = a[q[hh]]; } } void get_max(int a[], int b[], int tot) { int hh = 0, tt = -1; for(int i = 1; i <= tot; i ++ ) { while(hh <= tt && q[hh] < i - k + 1) hh ++ ; while(hh <= tt && a[q[tt]] <= a[i]) tt -- ; q[ ++ tt] = i; b[i] = a[q[hh]]; } } int main() { cin >> n >> m >> k; for(int i = 1; i <= n; i ++ ) { for(int j = 1; j <= m; j ++ ) { scanf("%d", &w[i][j]); } } for(int i = 1; i <= n; i ++ ) { get_min(w[i], row_min[i], m); get_max(w[i], row_max[i], m); } int res = 1e9; int a[N], b[N], c[N]; for(int i = k; i <= m; i ++ ) { for(int j = 1; j <= n; j ++ ) a[j] = row_min[j][i]; get_min(a, b, n); for(int j = 1; j <= n; j ++ ) a[j] = row_max[j][i]; get_max(a, c, n); for(int j = k; j <= n; j ++ ) res = min(res, c[j] - b[j]); } cout << res << endl; return 0; }
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long LL; const int N = 1010, MOD = 998244353; int n, m, A, B; int w[N][N]; int row_max[N][N], row_min[N][N]; int q[N]; void get_min(int a[], int b[], int tot, int k) { int hh = 0, tt = -1; for(int i = 1; i <= tot; i ++ ) { while(hh <= tt && q[hh] < i - k + 1) hh ++ ; while(hh <= tt && a[q[tt]] >= a[i]) tt -- ; q[ ++ tt] = i; b[i] = a[q[hh]]; } } void get_max(int a[], int b[], int tot, int k) { int hh = 0, tt = -1; for(int i = 1; i <= tot; i ++ ) { while(hh <= tt && q[hh] < i - k + 1) hh ++ ; while(hh <= tt && a[q[tt]] <= a[i]) tt -- ; q[ ++ tt] = i; b[i] = a[q[hh]]; } } int main() { scanf("%d%d%d%d", &n, &m, &A, &B); for(int i = 1; i <= n; i ++ ) { for(int j = 1; j <= m; j ++ ) { scanf("%d", &w[i][j]); } } for(int i = 1; i <= n; i ++ ) { get_min(w[i], row_min[i], m, B); get_max(w[i], row_max[i], m, B); } LL res = 0; int a[N], b[N], c[N]; for(int i = B; i <= m; i ++ ) { for(int j = 1; j <= n; j ++ ) a[j] = row_max[j][i]; get_max(a, b, n, A); for(int j = 1; j <= n; j ++ ) a[j] = row_min[j][i]; get_min(a, c, n, A); for(int j = A; j <= n; j ++ ) res = (res + (LL)b[j] * c[j]) % MOD; } printf("%lld\n", res); return 0; }