P2601 [ZJOI2009]对称的正方形(二维哈希)(二分)

洛谷传送门

题目描述

请添加图片描述

解析

做三个hash
分一下正方形边长的奇偶性
然后枚举中心点,二分边长即可
有点类似模拟赛那道红十字的题
我一开始觉得分奇偶好麻烦啊
为什么不直接枚举左上方的点二分呢?awa
很遗憾的是…
那样答案就没有单调性了啊…
我个傻子qwq

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
typedef unsigned long long ull;
const int N = 1050;
const ll mod=998244323;
const int p1=1e9+7;
const int p2=1e9+9;
int n,m;
int a[N][N];
ull h[N][N],h1[N][N],h2[N][N];//一左右,二上下 
ull mi1[N],mi2[N];
void hash(){
	mi1[0]=mi2[0]=1;
	for(int i=1;i<=max(n,m);i++){
		mi1[i]=mi1[i-1]*p1;
		mi2[i]=mi2[i-1]*p2;
	}
	
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			h[i][j]=h[i][j-1]*p1+a[i][j];
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			h[i][j]=h[i-1][j]*p2+h[i][j];
		}
	}
	
	for(int i=1;i<=n;i++){
		for(int j=m;j>=1;j--){
			h1[i][j]=h1[i][j+1]*p1+a[i][j];
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			h1[i][j]=h1[i-1][j]*p2+h1[i][j];
		}
	}
	
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			h2[i][j]=h2[i][j-1]*p1+a[i][j];
		}
	}
	for(int i=n;i>=1;i--){
		for(int j=1;j<=m;j++){
			h2[i][j]=h2[i+1][j]*p2+h2[i][j];
		}
	}
}

ull ask(int x1,int y1,int x2,int y2){
	return h[x2][y2]-h[x1-1][y2]*mi2[x2-x1+1]-h[x2][y1-1]*mi1[y2-y1+1]+h[x1-1][y1-1]*mi2[x2-x1+1]*mi1[y2-y1+1];
}
ull ask1(int x1,int y1,int x2,int y2){
	return h1[x2][y1]-h1[x1-1][y1]*mi2[x2-x1+1]-h1[x2][y2+1]*mi1[y2-y1+1]+h1[x1-1][y2+1]*mi2[x2-x1+1]*mi1[y2-y1+1];
}
ull ask2(int x1,int y1,int x2,int y2){
	return h2[x1][y2]-h2[x2+1][y2]*mi2[x2-x1+1]-h2[x1][y1-1]*mi1[y2-y1+1]+h2[x2+1][y1-1]*mi2[x2-x1+1]*mi1[y2-y1+1];
}
ll ans;
bool check(int x1,int y1,int x2,int y2){
	return ask(x1,y1,x2,y2)==ask1(x1,y1,x2,y2)&&ask(x1,y1,x2,y2)==ask2(x1,y1,x2,y2);
}
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]);
	}
	hash();
	//printf("%llu\n%llu\n%llu\n",ask(1,3,2,4),ask1(1,3,2,4),ask2(1,3,2,4));
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			int st=0,ed=min(n-i,min(m-j,min(i-1,j-1)));
			while(st<ed){
				int mid=(st+ed+1)>>1;
				if(check(i-mid,j-mid,i+mid,j+mid)) st=mid;
				else ed=mid-1;
			}
			ans+=st+1;
		}
	}
	for(int i=1;i<n;i++){
		for(int j=1;j<m;j++){
			int st=0,ed=min(n-i,min(m-j,min(i,j)));
			while(st<ed){
				int mid=(st+ed+1)>>1;
				if(check(i-mid+1,j-mid+1,i+mid,j+mid)) st=mid;
				else ed=mid-1;
			}
			ans+=st;
		}
	}
	printf("%lld\n",ans);
}
/*
5 5
4 2 4 4 4 
3 1 4 4 3 
3 5 3 3 3 
3 1 5 3 3 
4 2 1 2 4 
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值