[1282 JZOJ]资源勘探

Description
  教主要带领一群Orzer到一个雄奇地方勘察资源。 
  这个地方可以用一个n×m的矩阵A[i, j]来描述,而教主所在的位置则是位于矩阵的第1行第1列。
  矩阵的每一个元素A[i, j]均为一个不超过n×m的正整数,描述了位于这个位置资源的类型为第A[i, j]类。教主准备选择一个子矩阵作为勘察的范围,矩阵的左上角即为教主所在的(1, 1)。若某类资源k在教主勘察的范围内恰好出现一次。或者说若教主选择了(x, y)即第x行第y列作为子矩阵的右下角,那么在这个子矩阵中只有一个A[i, j](1≤i≤x,1≤j≤y)满足A[i, j]=k,那么第k类资源则被教主认为是稀有资源。 
  现在问题是,对于所有的(x, y),询问若(x, y)作为子矩阵的右下角,会有多少类不同的资源被教主认为是稀有资源。

Input
  输入的第一行包括两个正整数n和m,接下来n行,每行m个数字,描述了A[i, j]这个矩阵。

Output
  为了照顾Vijos脑残的输出问题,设B[i, j]表示仅包含前i行与前j列的子矩阵有多少个数字恰好出现一次,那么你所要输出所有B[i, j]之和mod 19900907。

Sample Input
2 3
1 2 3
3 1 2

Sample Output
10

【样例说明】
  对于右下角为(1,1)的子矩阵,仅包含数字1,所以答案为1。 
  对于右下角为(1,2)的子矩阵,数字1、2各出现一次,所以答案为2。 
  对于右下角为(1,3)的子矩阵,数字1、2、3各出现一次,所以答案为3。 
  对于右下角为(2,1)的子矩阵,数字1、3各出现一次,所以答案为2。 
  对于右下角为(2,2)的子矩阵,数字2、3各出现一次,但是数字1出现了两次,所以数字1不统计入答案,答案为2。 
  对于右下角为(2,3)的子矩阵,数字1、2、3均出现了两次,所以答案为0。
【数据说明】
  对于10%的数据,有N,M≤10; 
  对于20%的数据,有N,M≤20; 
  对于40%的数据,有N,M≤150; 
  对于50%的数据,A[I, J]≤1000; 
  对于70%的数据,有N,M≤800; 
  对于100%的数据,有N,M≤1100,A[I, J] ≤N×M。

Solution :n^4的方法显然是不可行的,仔细仔细思考之下会发现,对于一个矩阵里的某种资源A,只有第一次和第二次出现时是有意义的,于是 我们可以记录下A资源第一次出现的横纵坐标和第二次出现的纵坐标(当然也可以是纵坐标,这个无所谓),然后进行进行一次矩阵的扫 描,对于当前资源K,分类讨论:
Ⅰ:纵坐标比第一次出现的前,这样的的话,累加 ans = (ans + (i-x[k])*(y2[k]-y1[k])) % MO;
        Ⅱ: 纵坐标在第一二次之间 ans = (ans + (i-x[k])*(y2[k]-j)) % MO;
Ⅲ: 纵坐标在第二次之后?忽略,没有意义。
在统计答案之后,更新x和y1/y2。
这样统计的话,最后会剩下第一次和第二之间的答案没有累加,最后补上。
#include<cstdio>
#define MO 19900907
#define maxn 1111
using namespace std;
int k,ans,n,m,x[maxn*maxn],y1[maxn*maxn],y2[maxn*maxn];
int main(){
	scanf("%d%d",&n,&m);
	for (int i = 1;i <= n;i ++)
	for (int j = 1;j <= m;j ++){
		scanf("%d",&k);
		if (x[k] == 0){
			x[k] = i;
			y1[k] = j;
			y2[k] = m+1;
			continue;
		}
		if (j < y1[k]){
			ans = (ans + (i-x[k])*(y2[k]-y1[k])) % MO;
			y2[k] = y1[k];
			y1[k] = j;
			x[k] = i;
		} else {
			if (j < y2[k]){
				ans = (ans + (i-x[k])*(y2[k]-j)) % MO;
				y2[k] = j;
			}
		}
	}
	for (int i = 1;i <= n*m;i ++)
		if (x[i]){
			ans = (ans + (n+1-x[i])*(y2[i]-y1[i])) % MO;
		}
	printf("%d",ans);
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值