题目
2013 多校第二场 总结
题意:
有一个全由数字组成的矩阵,你要找到一个最大的子正方形,且这个正方形每一行每一列都是回文串。
解法一:
先假设正方形的边长len为奇数,那么对于中心点(x,y),所有的(x-len/2,y)~(x+len/2,y)(y同理)都要是至少回文半径为len/2+1的回文串。如果我们事先用manacher求出所有的行和列的回文半径的话,用rmq就知道(x-len/2,y)~(x+len/2,y)是否满足要求。据说不用rmq直接一个个验证更快。
len是偶数咋办?像manacher一样,填充原矩阵,就不用管奇偶的问题了,但是中心点所在的行列要么都是填充的,要么都不是填充的。
//Time:296ms
//Memory:17960KB //Length:2940B #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #define MAXN 620 #define INF 1007 using namespace std; int arr[MAXN][MAXN],tarr[MAXN]; short rad[2][MAXN][MAXN]; short rmq[2][MAXN][MAXN][10]; int ilog[MAXN]; void manacher(int a[],short r[],int len) { for(int i=0;i<len;++i) r[i]=0; r[0]=1; for(int i=1,j=0,k;i<len;) { while(a[i-j-1]==a[i+j+1]) ++j; r[i]=j; for(k=1;k<=j&&r[i-k]!=r[i]-k;++k) r[i+k]=min((int)r[i-k],r[i]-k); i+=k; j=max(0,j-k); } } void calrmq(int a[],short r[][10],int len) { int fe=log(len)/log(2)+1; for(int i=0;i<len;++i) r[i][0]=a[i]; for(int i=1,j=1;i<fe;++i,j*=2) for(int k=0;k<len;++k) if(k+j<len) r[k][i]=min(r[k][i-1],r[k+j][i-1]); else r[k][i]=r[k][i-1]; } bool check(int x,int y,int len) { int fe=ilog[len*2],ji; short tlen=INF; ji=1<<fe; tlen=min(tlen,min(rmq[0][y][x-len][fe],rmq[0][y][x+len-ji+1][fe])); tlen=min(tlen,min(rmq[1][x][y-len][fe],rmq[1][x][y+len-ji+1][fe])); return tlen>=len; } int main() { //freopen("/home/moor/Code/input","r",stdin); int ncase,n,m,ans,nn,mm,tmp; scanf("%d",&ncase); for(int i=1;i<MAXN;++i) ilog[i]=log(i*1.0)/log(2.0); while(ncase--) { ans=0; scanf("%d%d",&n,&m); nn=n*2+3,mm=m*2+3; memset(arr,-1,sizeof(arr)); for(int i=0;i<n;++i) for(int j=0;j<m;++j) scanf("%d",&arr[(i+1)*2][(j+1)*2]); n=n*2+3,m=m*2+3; for(int i=0;i<m;++i) arr[0][i]=-2,arr[n-1][i]=-3; for(int i=0;i<n;++i) arr[i][0]=-4,arr[i][m-1]=-5; arr[0][0]=-10,arr[n-1][0]=-11,arr[0][m-1]=-12,arr[n-1][m-1]=-13; for(int i=0;i<n;++i) manacher(arr[i],rad[0][i],m); for(int i=0;i<m;++i) { for(int j=0;j<n;++j) tarr[j]=arr[j][i]; manacher(tarr,rad[1][i],n); } for(int i=0;i<m;++i) { for(int j=0;j<n;++j) tarr[j]=rad[0][j][i]; calrmq(tarr,rmq[0][i],n-1); } for(int i=0;i<n;++i) { for(int j=0;j<m;++j) tarr[j]=rad[1][j][i]; calrmq(tarr,rmq[1][i],m-1); } for(int i=1;i<n;++i) for(int j=1;j<m;++j) { if((j%2==1)&&(i%2==0)||(j%2==0)&&(i%2==1)) continue; int l=1,r=min(min(i-1,n-i),min(j-1,m-j)),mid; while(l<r) { if(ans>=r) break; mid=(l+r+1)/2; if(check(i,j,mid)) l=mid; else r=mid-1; } ans=max(ans,l); } printf("%d\n",ans); } return 0; }
解法二:
二分(奇偶)+hash判断是否对称,其实好暴力的,但是竟然比上面的快,比赛没敢敲==
代码:
//time: 109ms //length: 2299B //memory: 3504K #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> using namespace std; #define maxn 305 int n,m,a[maxn][maxn]; long long int p[maxn],hash_r[2][maxn][maxn],hash_c[2][maxn][maxn]; void hash() { for (int i=1;i<=n;i++) { for (int j=1;j<=m;j++) hash_r[0][i][j]=hash_r[0][i][j-1]*131+a[i][j]; for (int j=m;j>=1;j--) hash_r[1][i][j]=hash_r[1][i][j+1]*131+a[i][j]; } for (int i=1;i<=m;i++) { for (int j=1;j<=n;j++) hash_c[0][j][i]=hash_c[0][j-1][i]*131+a[j][i]; for (int j=n;j>=1;j--) hash_c[1][j][i]=hash_c[1][j+1][i]*131+a[j][i]; } } long long int calr0(int r,int x,int y) {return hash_r[0][r][y]-hash_r[0][r][x-1]*p[y-x+1];} long long int calr1(int r,int x,int y) {return hash_r[1][r][x]-hash_r[1][r][y+1]*p[y-x+1];} long long int calc0(int c,int x,int y) {return hash_c[0][y][c]-hash_c[0][x-1][c]*p[y-x+1];} long long int calc1(int c,int x,int y) {return hash_c[1][x][c]-hash_c[1][y+1][c]*p[y-x+1];} bool check(int l) { for (int i=1;i+l-1<=n;i++) for (int j=1;j+l-1<=m;j++) { bool flag=true; for (int k=i;k<=i+l-1&&flag;k++) if (calr0(k,j,j+l/2-1)!=calr1(k,j+l/2+l%2,j+l-1)) flag=false; for (int k=j;k<=j+l-1&&flag;k++) if (calc0(k,i,i+l/2-1)!=calc1(k,i+l/2+l%2,i+l-1)) flag=false; if (flag) return true; } return false; } int main() { //freopen("/home/christinass/code/in.txt","r",stdin); p[0]=1; for (int i=1;i<=300;i++) p[i]=p[i-1]*131; int cas; scanf("%d",&cas); while (cas--) { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) scanf("%d",&a[i][j]); hash(); int l=0,r=n/2,ans=1; while (l<=r) { int mid=(l+r)>>1; if (check(2*mid+1)) ans=2*mid+1,l=mid+1; else r=mid-1; } l=(ans+1)/2,r=n/2; while (l<=r) { int mid=(l+r)>>1; if (check(2*mid)) ans=2*mid,l=mid+1; else r=mid-1; } printf("%d\n",ans); } return 0; }