Bootstrap

蓝桥杯真题 - 子矩阵 - 题解

题目链接:https://www.lanqiao.cn/problems/3521/learning/

个人评价:难度 3 星(满星:5)
前置知识:单调队列


整体思路

  • 考虑一维数组,取每 a a a 个滑动窗口的最大与最小值,可以用单调队列,二维就是用两次单调队列,用法如下:
  • 首先单独考虑每一行,找出每行内每个长度为 b b b 的滑动窗口的最大值与最小值,可以得到两个 n n n m − b + 1 m-b+1 mb+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 na+1 m − b + 1 m-b+1 mb+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;
}
;