NOIP-模拟试题之--矩阵

2018 NOIP 全套资料下载-提取码:072k

【问题描述】

Mr_he有一个 n* m 的矩阵,并且把1~n* m 这n*m填写在这个矩阵中(注意,每个整数在矩阵出现一次,且仅出现一次)。
  Mr_he同时约定一个矩阵权值等于这个子矩阵中的所有数的最小值。
  现在Mr_he想知道,在给出的矩阵中,权值为i的子矩阵可能有多少种?

【输入格式】

第一行, 两个整数 N, M。接下来的 N 行, 每行 M 个整数, 表示矩阵中的元素。

【输出格式】

N×M 行, 每行一个整数, 其中第 i 行的整数表示如果小 A 选择的子矩阵权值为 i, 他选择的子矩阵的种类数。

【输入样例】

2 3
2 5 1
6 3 4

【输出样例】

6
4
5
1
1
1

【样例解释】

balabalabala(好吧只是我不想粘图而已AuA)
  其他情况不再赘述!

【数据范围】

对于 30%的数据, 1<=N, M<=50;
对于全部的数据, 1<=N, M<=300。

———————————————————————————————————————————————————————
枚举矩阵的上下行(简单来说就是两根线),对于两根线之间形成的矩阵我们发现实际上只有每一列上最小的元素可能是以这两根线为上下界的矩阵中的最小值,所以压缩一下就成了一个序列了。

思考序列上怎么办,可以发现对于一个位置i的元素以其为最小值的线段的端点只可能在L[i]+1和R[i]-1,L[i]表示i左边第一个小于它的元素的下标,R[i]类似。然后以这个点i为最小元素的线段数量就应该是(i-L[i])*(R[i]-i)。这个直接用单调队列就好了。

也就是说对于每一种上下界枚举的情况我们都可以算出这个上下界对某一个元素最终答案的贡献,最后枚举完之后答案自然就出来了。QAQ

AC代码:

#include< iostream>
#include< cstdio>
#include< cstring>
#include< cstdlib>
#include< algorithm>
#include< cmath>
#include< queue>
#include< set>
#include< map>
#include< vector>
#include< cctype>
#define inf 1e9+5
using namespace std;
const int maxn=305;

int N,M,A[maxn][maxn];
int ans[maxn*maxn],minv[maxn];
struct data{ int pos,v; }q[maxn];
int front,rear,L[maxn],R[maxn];

void _scanf(int &x)
{
x=0;
char ch=getchar();
while(ch<‘0’||ch>‘9’) ch=getchar();
while(ch>=‘0’&&ch<=‘9’) x=x10+ch-‘0’,ch=getchar();
}
void data_in()
{
_scanf(N);_scanf(M);
for(int i=1;i<=N;i++)
for(int j=1;j<=M;j++) _scanf(A[i][j]);
}
void calc()
{
front=rear=0;
for(int j=1;j<=M;j++)
{
while(front!=rear&&q[rear-1].v>minv[j]) rear–;
L[j]= front!=rear ? q[rear-1].pos : 0;
q[rear++]=(data){j,minv[j]};
}
front=rear=0;
for(int j=M;j>=1;j–)
{
while(front!=rear&&q[rear-1].v>minv[j]) rear–;
R[j]= front!=rear ? q[rear-1].pos : M+1;
q[rear++]=(data){j,minv[j]};
}
}
void work()
{
for(int i=1;i<=N;i++)
{
for(int j=1;j<=M;j++) minv[j]=inf;
for(int ii=i;ii<=N;ii++)
{
for(int j=1;j<=M;j++) minv[j]=min(minv[j],A[ii][j]);
calc();
for(int j=1;j<=M;j++)
ans[minv[j]]+=(j-L[j])
(R[j]-j);
}
}
for(int i=1;i<=N*M;i++)
printf("%d\n",ans[i]);
}
int main()
{
freopen(“matrix.in”,“r”,stdin);
freopen(“matrix.out”,“w”,stdout);
data_in();
work();
return 0;
}


原文:https://blog.csdn.net/qq_39439314/article/details/78174152

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值