测试地址:对称的正方形
作法: 本题须要用到Manacher+RMQ。
首先,咱们想到枚举正方形的对称轴,求对称轴交点为某个点时的最大正方形大小。为了方便,咱们模仿Manacher算法,把矩阵用
扩充成一个
的矩阵,这样咱们就只用考虑中心点是整数坐标的状况了。
咱们思考怎么判断一个中心点向外延伸
的长度的正方形是否合法。首先正方形要左右对称,那么中间的对称轴上的
个点,它们在各自行中以它们为中心的最长回文子串长度,应该大于等于
,显然这个长度能够用Manacher算法算出。上下对称也是同理。但是暴力判断是
的,会爆炸,咱们须要找到另外一种思路。
咱们分开考虑一个中心点的上、下、左、右四个边界,问题就转化为,求右端点给定时,使得区间内数都大于等于某个数的最左的左端点是哪个。注意到右端点右移时,左端点也是单调右移的,这时咱们配合RMQ就能够解决这个问题了。求出某中心点的四个边界后,求最小值就是这个中心点的边界了,这以后讨论答案就很简单了。
因而总的时间复杂度为
,能够经过此题。
如下是本人代码:php
#include <bits/stdc++.h> using namespace std; int n,m,a[2010][2010]={0},r[2010][2010],c[2010][2010]; int s[2010],len[2010],mn[2010][15],p[2010]; int lft[2010][2010],rht[2010][2010],up[2010][2010],down[2010][2010]; void Manacher(int n) { int center=1,rht=1; len[1]=1; for(int i=2;i<=n;i++) { if (i>rht||i+len[2*center-i]-1>=rht) { len[i]=(i>rht)?0:(rht-i+1); while(i-len[i]>=1&&i+len[i]<=n&&s[i-len[i]]==s[i+len[i]]) len[i]++; center=i; rht=i+len[i]-1; } else len[i]=len[2*center-i]; } } void init_RMQ(int n) { p[0]=0; for(int i=1;i<=n;i++) { if ((1<<(p[i-1]+1))<i) p[i]=p[i-1]+1; else p[i]=p[i-1]; mn[i][0]=s[i]; } for(int i=1;i<=12;i++) for(int j=1;j<=n-(1<<i)+1;j++) mn[j][i]=min(mn[j][i-1],mn[j+(1<<(i-1))][i-1]); } int query(int l,int r) { int x=r-l+1; return min(mn[l][p[x]],mn[r-(1<<p[x])+1][p[x]]); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&a[2*i-1][2*j-1]); n=2*n-1,m=2*m-1; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) s[j]=a[i][j]; Manacher(m); for(int j=1;j<=m;j++) r[i][j]=len[j]; } for(int i=1;i<=m;i++) { for(int j=1;j<=n;j++) s[j]=a[j][i]; Manacher(n); for(int j=1;j<=n;j++) c[j][i]=len[j]; } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) s[j]=c[i][j]; init_RMQ(m); int x=1; for(int j=1;j<=m;j++) { while(query(x,j)<j-x+1) x++; lft[i][j]=j-x+1; } x=m; for(int j=m;j>=1;j--) { while(query(j,x)<x-j+1) x--; rht[i][j]=x-j+1; } } for(int i=1;i<=m;i++) { for(int j=1;j<=n;j++) s[j]=r[j][i]; init_RMQ(n); int x=1; for(int j=1;j<=n;j++) { while(query(x,j)<j-x+1) x++; up[j][i]=j-x+1; } x=n; for(int j=n;j>=1;j--) { while(query(j,x)<x-j+1) x--; down[j][i]=x-j+1; } } int ans=0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if (i%2==j%2) { int x=min(min(up[i][j],down[i][j]),min(lft[i][j],rht[i][j])); ans+=(x+(i%2))>>1; } printf("%d",ans); return 0; }