蓝桥杯 子矩阵

题目描述

给定一个 n × m (n 行 m 列)的矩阵。

设一个矩阵的价值为其所有数中的最大值和最小值的乘积。求给定矩阵的所有大小为 a × b (a 行 b 列)的子矩阵的价值的和。

答案可能很大,你只需要输出答案对 998244353 取模后的结果。

输入格式

输入的第一行包含四个整数分别表示 n, m, a, b ,相邻整数之间使用一个空格分隔。

接下来 n 行每行包含 m 个整数,相邻整数之间使用一个空格分隔,表示矩阵中的每个数 Ai, j 。

输出格式

输出一行包含一个整数表示答案。

样例输入

复制

2 3 1 2
1 2 3
4 5 6

样例输出

复制

58
#include<iostream>
#define endl '\n'
#define INF 0x3f3f3f3f
#define int long long
using namespace std;
const int mod = 998244353;
const int N = 1e3 + 10;
int g[N][N];//存储输入的信息
int q[N];//存储窗口中的信息
int line_max[N][N], line_min[N][N];//一行/一竖窗口对应的最大值
int maxv[N][N], minv[N][N];//以下标为起点的,a*b矩阵中的最大值/最小值
int a, b, n, m;
void solve()
{
	cin >> n >> m >> a >> b;
	for (int i = 0; i < n; i++)
		for (int j = 0; j < m; j++)
			cin >> g[i][j];
	for (int i = 0; i < n; i++)
	{
		int h = 0, t = -1;
		for (int j = 0; j < m; j++)
		{
			if (h <= t && q[h] < j - b + 1)//判断h这个位置还在不在窗口内
				h++;
			while (h <= t && g[i][q[t]]<= g[i][j])//队尾的这个数的数值小于当前这个数,那队尾的这个数就可以出队了
				t--;
			q[++t] = j;
			if (j - b + 1 >= 0)
				line_max[i][j - b + 1] = g[i][q[h]];

		}
	}
	for (int j = 0; j < m; j++)
	{
		int h = 0, t = -1;
		for (int i = 0; i < n; i++)
		{
			if (h <= t && q[h] < i - a + 1)
				h++;
			while (h <= t && line_max[q[t]][j] <= line_max[i][j])
				t--;
			q[++t] = i;
			if (i - a + 1 >= 0)
				maxv[i - a + 1][j] = line_max[q[h]][j];
		}
	}
	for (int i = 0; i < n; i++)
	{
		int h = 0, t = -1;
		for (int j = 0; j < m; j++)
		{
			if (h <= t && q[h] < j - b + 1)//判断h这个位置还在不在窗口内
				h++;
			while (h <= t && g[i][q[t]]>= g[i][j])//队尾的这个数的数值小于当前这个数,那队尾的这个数就可以出队了
				t--;
			q[++t] = j;
			if (j - b + 1 >= 0)
				line_min[i][j - b + 1] = g[i][q[h]];

		}
	}
	for (int j = 0; j < m; j++)
	{
		int h = 0, t = -1;
		for (int i = 0; i < n; i++)
		{
			if (h <= t && q[h] < i - a + 1)
				h++;
			while (h <= t && line_min[q[t]][j] >= line_min[i][j])
				t--;
			q[++t] = i;
			if (i - a + 1 >= 0)
				minv[i - a + 1][j] = line_min[q[h]][j];
		}
	}
	int ans = 0;
	for(int i=0;i<n;i++)
		for (int j = 0; j < m; j++)
		{
			ans = (ans + maxv[i][j] * minv[i][j]%mod) % mod;
		}
	cout <<ans<< endl;
}
signed main()
{
	ios::sync_with_stdio;
	cin.tie(0);
	cout.tie(0);
	int t= 1;
	while (t--)
		solve();
	return 0;
}

代码解析

if (h <= t && q[h] < j - b + 1)//判断h这个位置还在不在窗口内

这里你可以画个图来模拟窗口的滑动,(满窗口同时数据够大1的情况下)当窗口中一个元素离开窗口就必定有另一个元素进窗口,j-b+1相当于是当前窗口的起点,如果这个值比h(设置的起点的值更大)就说明当前起点已经不在窗口内了,就需要向后移动。

while (h <= t && g[i][q[t]]<= g[i][j])

t--;

在满足窗口内元素的前提下判断当前窗口结尾元素于当前元素的大小,因为是找出最大值所以只要当前当前元素比窗口末尾元素大,当前末尾元素就需要更新了。

这里不需要担心窗口的滑动会影响到最大值的选取,因为这里的窗口末尾更新与之前的q[h] < j - b + 1、和h<=t限制了最大值的选取。

q[++t] = j;

这里解决了一开始t=-1的更新问题,++t保留了原来t位置的信息,当原来的t位置出窗口,现在的t就有机会成为最大的。

if (j - b + 1 >= 0)
                line_max[i][j - b + 1] = g[i][q[h]];

这里更新以i,j为起点构成的一维矩阵的最大值。

其他代码都可以通过上述代码理解。

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值