蓝桥杯省赛 子矩阵 单调队列
⭐ 子矩阵
输入案例
2 3 1 2
1 2 3
4 5 6
输出案例
58
⭐ 二维单调队列 O(nm)
⭐ 单调队列存的是下标
import java.util.Scanner;
public class Main
{
static int N = 1010, mod = 998244353;
static int n, m, A, B;
static int[][] g = new int[N][N];
static int[][] rmax = new int[N][N];// 第i行以第 j 列为右端点的区间最大值
static int[][] rmin = new int[N][N];
static int[] q = new int[N];
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
A = sc.nextInt();
B = sc.nextInt();
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
g[i][j] = sc.nextInt();
// 预处理每一行的小区间最值
for (int i = 0; i < n; i++)
{
getMax(g[i], rmax[i], m, B);
getMin(g[i], rmin[i], m, B);
}
long res = 0;
int[] a = new int[N];// 记录每一行的子矩阵区间的最值
int[] b = new int[N];// 记录当前子矩阵的最大值
int[] c = new int[N];// 记录当前子矩阵的最小值
for (int i = B - 1; i < m; i++)// i 枚举横向每一个区间的终点
{
for (int j = 0; j < n; j++)// j 枚举每一行
a[j] = rmax[j][i];
getMax(a, b, n, A);// n 行,a是每一行的最大值,A 是子矩阵的行数,找出子矩阵的最大值
for (int j = 0; j < n; j++)
a[j] = rmin[j][i];
getMin(a, c, n, A);
for (int j = A - 1; j < n; j++)
{
res = (res + (long) b[j]% mod * (long)c[j]% mod ) % mod;
}
}
System.out.println(res);
}
// 单调递增
private static void getMin(int[] a, int[] b, int tot, int k)
{
int hh = 0;
int tt = -1;
for (int i = 0; i < tot; i++)// 枚举每一个起点
{
if (hh <= tt && q[hh] <= i - k)
hh++;
while (hh <= tt && a[i] <= a[q[tt]])
tt--;
q[++tt] = i;
b[i] = a[q[hh]];// 队头元素是最小值
}
}
// 单调递减队列
private static void getMax(int[] a, int[] b, int tot, int k)
{
int hh = 0, tt = -1;
for (int i = 0; i < tot; i++)// 枚举区间的起点
{
if (hh <= tt && q[hh] <= i - k)// 队头超出区间边界
hh++;
while (hh <= tt && a[i] >= a[q[tt]])
tt--;
q[++tt] = i;
b[i] = a[q[hh]];// 队头元素是最大值
}
}
}