题目大意:
给你一个n*m(n,m<=1100)的矩阵,其中a[i][j]的值均不超过n*m,需要求出所有以[1][1]为左上顶点的子矩阵中只出现一次的数的个数总和,ans对19900907取模.
解题思路:
如果是对于求以单个点作为右下顶点的矩阵中只出现一次的数的个数,我们便可以极具洞察力(naive)地想到用两层循环求解.通过此思路,我们可以对任意一个点进行扫描,整体复杂度(O((nm)²)),显然过不去所有数据点.因此,我们需要想办法优化.
分析上面的高复杂度算法,直观看出复杂度高的原因是因为我们对单个点进行了重复多次的扫描,此时便想到通过累计每个点值的贡献来降低复杂度.
那么,接下来要解决的问题就是如何求出每个点值的贡献.
观察这么一组数据:
2 2 1 5 6 5
2 3 2 4 2 3
2 3 4 2 1 3
3 1 5 2 4 5
1 2 5 3 5 7
2 1 2 5 3 4
其中标红的点为数字'1'的贡献.不难看出,如果值为1的两个值同为val的两个限制点A,B之间没有任何值为val的点,那么就会产生等价于两个限制点之间夹的矩阵大小的贡献.所以我们只需求出第一个限制点的xy坐标和第二个限制点的y坐标.
对于点P[i][j],其值为val.如果当前数字是第一次出现,那么令x[val]=i,y1[val]=j,y2[val]=m+1;
如果不是第一次出现,那么我们累计贡献并更新限制点,具体方法如下:
如果j<y1[val],那将(y2[val]-y1[val])*(i-x[val])计入ans,然后将A更新成为限制点B,P更新成为限制点A,即y2[val]=y1[val],y1[val]=j,x[val]=i;
如果y1[val]<=j<y2[val],那么将(y2[val]-j)*(i-x[val])计入ans,然后将P更新成为限制点B,即y2[val]=j;
最后统计所有限制点的贡献,对于每个val,将(y2[val]-y1[val])*(n-x[val]+1)计入ans.
简单模拟可知,这种做法不会遗漏任意数值的任何贡献.
code:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> #include<queue> #define mod 19900907 #define R register #define next exnt #define debug puts("mlg") using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; inline ll read(); inline void write(ll x); inline void writesp(ll x); inline void writeln(ll x); ll n,m; ll x[1211000],Y1[1211000],Y2[1211000]; ll ans=0; ll val; int main(){ n=read();m=read(); for(R ll i=1;i<=n;i++){ for(R ll j=1;j<=m;j++){ val=read(); if(!x[val]){ x[val]=i; Y1[val]=j; Y2[val]=m+1; continue; } if(j<Y1[val]){ ans+=(Y2[val]-Y1[val])*(i-x[val]); ans%=mod; Y2[val]=Y1[val]; Y1[val]=j; x[val]=i; } else{ if(j<Y2[val]){ ans+=(Y2[val]-j)*(i-x[val]); ans%=mod; Y2[val]=j; } } } } for(R ll i=1;i<=n*m;i++){ if(x[i]){ ans+=(Y2[i]-Y1[i])*(n-x[i]+1); ans%=mod; } } writeln(ans); } inline ll read(){ ll x=0,t=1;char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') t=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ x=x*10+ch-'0'; ch=getchar(); } return x*t; } inline void write(ll x){ if(x<0){putchar('-');x=-x;} if(x<=9){putchar(x+'0');return;} write(x/10);putchar(x%10+'0'); } inline void writesp(ll x){ write(x);putchar(' '); } inline void writeln(ll x){ write(x);putchar(' '); }