题目链接:https://www.lanqiao.cn/problems/3521/learning/
个人评价:难度 3 星(满星:5)
前置知识:单调队列
整体思路
- 考虑一维数组,取每 a a a 个滑动窗口的最大与最小值,可以用单调队列,二维就是用两次单调队列,用法如下:
- 首先单独考虑每一行,找出每行内每个长度为 b b b 的滑动窗口的最大值与最小值,可以得到两个 n n n 行 m − b + 1 m-b+1 m−b+1 列的二维数组,分别设为 m x mx mx 与 m n mn mn 数组;
- 再对以上数组单独考虑每一列,找出每列内每个长度为 a a a 的滑动窗口的最大值与最小值,注意需要从 m x mx mx 数组中找最大值, m n mn mn 数组中找最小值;
- 这样得到的 n − a + 1 n-a+1 n−a+1 行 m − b + 1 m-b+1 m−b+1 列的数组的第 i i i 行第 j j j 列的最大 / 最小值,对应的就是原数组中以第 i i i 行第 j j j 列个元素为左上角,大小为 a × b a \times b a×b 的子矩阵的最大 / 最小值。
过题代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL MOD = 998244353;
const int maxn = 1000 + 100;
int n, m, a, b;
LL ans;
LL num[maxn][maxn], mx[maxn][maxn], mn[maxn][maxn];
deque<LL> mxque, mnque;
int main() {
#ifdef ExRoc
freopen("test.txt", "r", stdin);
#endif // ExRoc
cin >> n >> m >> a >> b;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
cin >> num[i][j];
}
}
for (int i = 1; i <= n; ++i) {
mxque.clear();
mnque.clear();
for (int j = 1; j < b; ++j) {
while (!mxque.empty() && num[i][mxque.front()] <= num[i][j]) {
mxque.pop_front();
}
mxque.push_front(j);
while (!mnque.empty() && num[i][mnque.front()] >= num[i][j]) {
mnque.pop_front();
}
mnque.push_front(j);
}
for (int j = b; j <= m; ++j) {
while (!mxque.empty() && num[i][mxque.front()] <= num[i][j]) {
mxque.pop_front();
}
mxque.push_front(j);
mx[i][j - b + 1] = num[i][mxque.back()];
if (mxque.back() == j - b + 1) {
mxque.pop_back();
}
while (!mnque.empty() && num[i][mnque.front()] >= num[i][j]) {
mnque.pop_front();
}
mnque.push_front(j);
mn[i][j - b + 1] = num[i][mnque.back()];
if (mnque.back() == j - b + 1) {
mnque.pop_back();
}
}
}
for (int j = 1; j + b - 1 <= m; ++j) {
mxque.clear();
mnque.clear();
for (int i = 1; i < a; ++i) {
while (!mxque.empty() && mx[mxque.front()][j] <= mx[i][j]) {
mxque.pop_front();
}
mxque.push_front(i);
while (!mnque.empty() && mn[mnque.front()][j] >= mn[i][j]) {
mnque.pop_front();
}
mnque.push_front(i);
}
for (int i = a; i <= n; ++i) {
while (!mxque.empty() && mx[mxque.front()][j] <= mx[i][j]) {
mxque.pop_front();
}
mxque.push_front(i);
mx[i - a + 1][j] = mx[mxque.back()][j];
if (mxque.back() == i - a + 1) {
mxque.pop_back();
}
while (!mnque.empty() && mn[mnque.front()][j] >= mn[i][j]) {
mnque.pop_front();
}
mnque.push_front(i);
mn[i - a + 1][j] = mn[mnque.back()][j];
if (mnque.back() == i - a + 1) {
mnque.pop_back();
}
}
}
for (int i = 1; i + a - 1 <= n; ++i) {
for (int j = 1; j + b - 1 <= m; ++j) {
ans = (ans + mn[i][j] * mx[i][j] % MOD) % MOD;
}
}
cout << ans << endl;
return 0;
}