【题解】[ZJOI2009] 对称的正方形

官方题解写得像 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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值