蓝桥杯省赛 子矩阵 单调队列

⭐ 子矩阵
蓝桥杯省赛 子矩阵 单调队列_第1张图片
输入案例

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]];// 队头元素是最大值
		}
	}
}

你可能感兴趣的:(算法题解,蓝桥杯)