官方题解写得像 shit
首先看到 对称 ,不难想到二维 hash。
那么我们判断左右和上下翻转后的矩形是不是和原来的完全相同。
枚举中心位置,然后二分。
#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;
const int N=1005;
const int P=13333331;
const int Q=1333333331;
//sol: 二维 hash
int n,m;
int a[N][N],b[N][N],c[N][N],tot1,tot2;
ull f1[N][N],f2[N][N],f3[N][N],f4[N],f5[N];
bool check1(int A,int B,int C,int D,int A2,int B2,int C2,int D2) {
return (f1[C][D]-f1[A-1][D]-f1[C][B-1]+f1[A-1][B-1])*f4[A2-1]*f5[B2-1]==(f2[C2][D2]-f2[A2-1][D2]-f2[C2][B2-1]+f2[A2-1][B2-1])*f4[A-1]*f5[B-1];
}
bool check2(int A,int B,int C,int D,int A2,int B2,int C2,int D2) {
return (f1[C][D]-f1[A-1][D]-f1[C][B-1]+f1[A-1][B-1])*f4[A2-1]*f5[B2-1]==(f3[C2][D2]-f3[A2-1][D2]-f3[C2][B2-1]+f3[A2-1][B2-1])*f4[A-1]*f5[B-1];
}
bool check(int A,int B,int C,int D) {
return check1(A,B,C,D,n-C+1,B,n-A+1,D)&&check2(A,B,C,D,A,m-D+1,C,m-B+1);
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
scanf("%d",&a[i][j]);
b[n-i+1][j]=c[i][m-j+1]=a[i][j];
}
}
f4[0]=f5[0]=1;
for(int i=1;i<=n;i++) f4[i]=f4[i-1]*P;
for(int i=1;i<=m;i++) f5[i]=f5[i-1]*Q;
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
f1[i][j]=f1[i][j-1]+f1[i-1][j]-f1[i-1][j-1]+a[i][j]*f4[i]*f5[j];
f2[i][j]=f2[i][j-1]+f2[i-1][j]-f2[i-1][j-1]+b[i][j]*f4[i]*f5[j];
f3[i][j]=f3[i][j-1]+f3[i-1][j]-f3[i-1][j-1]+c[i][j]*f4[i]*f5[j];
}
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
int l=1,r=min(min(i-1,n-i),min(j-1,m-j)),res=0;
while(l<=r) {
int mid=(l+r)/2;
if(check(i-mid,j-mid,i+mid,j+mid)) res=mid,l=mid+1;
else r=mid-1;
}
tot1+=res+1;
}
}
for(int i=1;i<n;i++) {
for(int j=1;j<m;j++) {
if(a[i][j]==a[i+1][j]&&a[i][j]==a[i][j+1]&&a[i][j]==a[i+1][j+1]) {
int l=1,r=min(min(i-1,j-1),min(n-i-1,m-j-1)),res=0;
while(l<=r) {
int mid=(l+r)/2;
if(check(i-mid,j-mid,i+mid+1,j+mid+1)) res=mid,l=mid+1;
else r=mid-1;
}
tot2+=res+1;
}
}
}
printf("%d",tot1+tot2);
}