抹茶学长给的标程可以被卡到O(N2M2)???
考虑二分答案+暴力check+离散化+卡常数
首先进行离散化,其实判重的话会更快,但是由于矩阵元素大小太大了,hash判重MLE,所以我就直接记录了NM个元素之后排序,即可二分离散化后数组中的下标。
二分离散化数组的下标,对于每一个下标考虑暴力check。设数组pd,当pd[i][j]=1时,表示F(i,j)>=mid,对于需验证的答案mid,首先令mid--,这样只需判断比mid大的数即可(常数优化)。之后用O(NM)的时间计算pd数组(可以用bitset进行常常数优化,蒟蒻这里不太会,直接开的bool数组)这里有一个很重要的优化,如果一行中1的数量小于2,它一定不能作为矩阵的上界和下界,O(N)记录即可。最后枚举矩阵上下界,设上界为i行,下界为j行,枚举k列。当pd[i][k]==pd[j][k]==1时,可以作为矩阵的两个角,如果两列中列有两组以上这样的情况,那么答案mid是可行的,传回成功信息,如果对于所有行都不行的话,那么传回失败信息。最后,能用快读快写的地方尽量用快读快写,能用位运算的地方尽量用位运算。
加入以上优化后我成功地把O(N2M(logNM))的算法(N,M≤1000)卡到了568ms(逃)
参考代码如下:
1 #include<iostream> 2 #include<bitset> 3 #include<cstdio> 4 #include<algorithm> 5 #include<cstdlib> 6 using namespace std; 7 inline int read() 8 { 9 int x=0; 10 bool f=1; 11 char c=getchar(); 12 for(; !isdigit(c); c=getchar()) if(c=='-') f=0; 13 for(; isdigit(c); c=getchar()) x=(x<<3)+(x<<1)+c-'0'; 14 if(f) return x; 15 return 0-x; 16 } 17 int n,m,f[1010][1010],maxn=-1073741822,minn=1073741822,v[1000001]; 18 inline bool check(int x) 19 { 20 x--; 21 bool p[1010]={},pd[1010][1010]={}; 22 int ct[1010]={}; 23 for(int i=1;i<=n;i++) 24 for(int j=1;j<=m;j++) 25 if(f[i][j]>x)pd[i][j]=1,ct[i]++; 26 for(int i=1;i<=n;i++) 27 { 28 if(ct[i]<2)p[i]=1; 29 } 30 for(int i=1;i<n;i++) 31 { 32 if(p[i])continue; 33 for(int j=i+1;j<=n;j++) 34 { 35 if(p[j])continue; 36 int cnt=0; 37 for(int k=1;k<=m;k++) 38 if(pd[i][k]&pd[j][k]) 39 { 40 cnt++; 41 if(cnt>1) return 1; 42 } 43 } 44 } 45 return false; 46 } 47 int main() 48 { 49 //srand(20050923); 50 int t=0; 51 n=read();m=read(); 52 for(int i=1;i<=n;i++) 53 for(int j=1;j<=m;j++) 54 { 55 f[i][j]=read(); 56 v[++t]=f[i][j]; 57 maxn=max(maxn,f[i][j]); 58 minn=min(minn,f[i][j]); 59 } 60 sort(v+1,v+t+1); 61 int l=1,r=t,mid,tot=0; 62 while(l<=r) 63 { 64 mid=(l+r)>>1; 65 if(check(v[mid])) l=mid+1; 66 else r=mid-1; 67 } 68 cout<<v[l-1]; 69 return 0; 70 }