[20200715NOIP提高組模擬T3]資源勘探
題目大意:
給你一個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('\n'); }