Description
回文序列是指左右对称的序列。例如1 2 3 2 1是回文序列,但是1 2 3 2 2就不是。我们会给定一个N×M的矩阵,你需要从这个矩阵中找出一个P×P的子矩阵,使得这个子矩阵的每一列和每一行都是回文序列。
Input
第一行有两个正整数N, M。
接下来是n行,代表一个N×M的矩阵,矩阵的每个元素都是值不超过31415926的正整数。
Output
输出符合条件的子矩阵的最大大小P。
Sample Input
5 10
1 2 3 3 2 4 5 6 7 8
1 2 3 3 2 4 5 6 7 8
1 2 3 3 2 4 5 6 7 8
1 2 3 3 2 4 5 6 7 8
1 2 3 9 10 4 5 6 7 8
Sample Output
4
Data Constraint
对于20%数据 1 ≤ N, M ≤ 10
对于所有数据 1 ≤ N, M ≤ 300
Solution
据说N^3、N^4、N^5都可以过。
但是我打了一个N^2 log^2 n的方法。
首先将整个矩阵每两个数中间加一个0,对于每行每列做一遍马拉车。
设f[ i ][ j ]表示以第i行第j列为中心的同一行的最长的回文串的长度,g[ i ][ j ]表示同一列的最长回文串的长度。
预处理是N^2的。
然后二分答案。
对于一个点,我们先将同一行的g[][]加入一颗线段树中(或者预处理出RMQ然后用倍增),表示所有的数在列上的最长回文串长度已经知道了,然后判断以当前这个点为中心的回文串的长度是否大于等于mid,再判断以当前点为中心的长度刚好大于等于mid的这一段区间中g[][]的最小值是否大于等于mid,如果是,则mid合法。
因为对于每一列都已经是一个回文串了,如果当前点往左右扩展也是一个回文串的话,那么整个矩阵就都合法了。
注意分开判断回文串长度为奇偶的情况
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define I int
#define ll long long
#define ls x<<1
#define rs ls|1
#define F(i,a,b) for(I i=a;i<=b;i++)
#define Fd(i,a,b) for(I i=a;i>=b;i--)
#define N 610
using namespace std;
I n,m,a[N][N],f[N][N],g[N][N],tr[N<<2],ans=0,k;
void ins(I k,I v,I x=1,I l=1,I r=(m+1)>>1){
if(l==r){tr[x]=v;return;}
I M=l+r>>1;
if(k<=M) ins(k,v,ls,l,M);else ins(k,v,rs,M+1,r);
tr[x]=min(tr[ls],tr[rs]);
}
I fnd(I l2,I r2,I x=1,I l=1,I r=(m+1)>>1){
if(r<l2||l>r2) return N;
if(l>=l2&&r<=r2){return tr[x];}
I M=l+r>>1;
return min(fnd(l2,r2,ls,l,M),fnd(l2,r2,rs,M+1,r));
}
I check(I x){
F(i,1,n){
memset(tr,0x3f3f,sizeof tr);
F(j,1,m) if(j&1){ins((j+1)>>1,g[i][j]-1);}
if(i&1){
F(j,1,m) if((j&1)&&(f[i][j]-1>=x)){
if(fnd(((j+1)>>1)-x/2+1,((j+1)>>1)+x/2-1)>=x) return 1;
}
}
else{
F(j,1,m) if((!(j&1))&&f[i+1][j]-1>=x){
if(fnd((j>>1)-x/2+1,((j+1)>>1)+x/2-1)>=x) return 1;
}
}
}
return 0;
}
I main(){
scanf("%d%d",&n,&m);n=n*2-1,m=m*2-1;I l,r,mid;
F(i,1,n) if(i&1){
F(j,1,m) if(j&1) scanf("%d",&a[i][j]);
}
F(i,1,n) if(i&1){
l=r=0;
F(j,1,m){
k=(j>r)?1:min(f[i][l+r-j],r-j);
while(j-k>=0&&j+k<=m+1&&a[i][j-k]==a[i][j+k]) k++;
f[i][j]=k--;
if(j+k>r){l=j-k,r=j+k;}
}
}
F(j,1,m) if(j&1){
l=r=0;
F(i,1,n){
k=(i>r)?1:min(g[l+r-i][j],r-i);
while(i-k>=0&&i+k<=n+1&&a[i-k][j]==a[i+k][j]) k++;
g[i][j]=k--;
if(i+k>r){l=i-k,r=i+k;}
}
}
l=0,r=(min(n,m)+1)>>1;
while(l<=r){
mid=l+r>>1;
if(check(mid)) l=(ans=mid)+1;
else r=mid-1;
}
printf("%d\n",ans);
return 0;
}