luoguP2601 对称的正方形

题目描述

给出一个数字矩形,求这个矩形中有多少个子正方形满足上下对称、左右对称。

思路

我们可以用3个哈希数组 \(a\ b\ c\) 分别表示矩形从左上往右下看,从左下往右上看,从右上往左下看的样子,那么我们可以得到:

  1. 如果正方形 \((x,y,u,v)\)(即以 \((x,y)\) 这一格为左上角,\((u,v)\) 为右下角的正方形)左右对称,那么它从左上往右下看和从右上往左下看是一样的,即 \(a\) 哈希数组和 \(c\) 哈希数组相等。
  2. 如果正方形 \((x,y,u,v)\) 上下对称,那么它从左上往右下看和从左下往右上看是一样的,即 \(a\) 哈希数组和 \(b\) 哈希数组相等。

如果两条条件都满足,那么这个正方形就是对称的正方形。

我们可以分边有奇数个节点和边有偶数个节点的正方形枚举它的中心点,显然,答案是具有单调性的,于是我们就可以进行二分答案求值。

实现

我们通过处理出 \((1,1,i,j)\) 矩形的哈希值,那么就可以通过类似前缀和的方式来分别得到正方形 \((x,y,u,v)\)\(a\ b\ c\) 三个值。柿子如下:

q.a=a[u][v]-a[u][y-1]*p1[v-y+1]-a[x-1][v]*p2[u-x+1]+a[x-1][y-1]*p1[v-y+1]*p2[u-x+1];
q.b=b[x][v]-b[x][y-1]*p1[v-y+1]-b[u+1][v]*p2[u-x+1]+b[u+1][y-1]*p1[v-y+1]*p2[u-x+1];
q.c=c[u][y]-c[u][v+1]*p1[v-y+1]-c[x-1][y]*p2[u-x+1]+c[x-1][v+1]*p1[v-y+1]*p2[u-x+1];

然后进行判断即可。

Code

#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
namespace EMT{
	#define pf printf
	#define F(i,a,b) for(register int i=a;i<=b;i++)
	#define D(i,a,b) for(register int i=a;i>=b;i--)
	typedef unsigned long long ull;
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline int min(int a,int b){return a<b?a:b;}inline int max(int a,int b){return a>b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
	const int N=1e3+10;
	int n,m,num[N][N],tot;ull a[N][N],b[N][N],c[N][N],p1[N],p2[N];
	struct node{ull a,b,c;}q;
	inline void get(int x,int y,int u,int v){
		q.a=a[u][v]-a[u][y-1]*p1[v-y+1]-a[x-1][v]*p2[u-x+1]+a[x-1][y-1]*p1[v-y+1]*p2[u-x+1];
		q.b=b[x][v]-b[x][y-1]*p1[v-y+1]-b[u+1][v]*p2[u-x+1]+b[u+1][y-1]*p1[v-y+1]*p2[u-x+1];
		q.c=c[u][y]-c[u][v+1]*p1[v-y+1]-c[x-1][y]*p2[u-x+1]+c[x-1][v+1]*p1[v-y+1]*p2[u-x+1];
	}
	inline short main(){
		p1[0]=p2[0]=1;
		n=read(),m=read();
		F(i,1,N-10)p1[i]=p1[i-1]*131,p2[i]=p2[i-1]*13331;
		F(i,1,n)F(j,1,m)num[i][j]=read();
        	//求哈希值
		F(i,1,n)F(j,1,m)a[i][j]=a[i][j-1]*131+a[i-1][j]*13331-a[i-1][j-1]*131*13331+num[i][j];
		D(i,n,1)F(j,1,m)b[i][j]=b[i][j-1]*131+b[i+1][j]*13331-b[i+1][j-1]*131*13331+num[i][j];
		F(i,1,n)D(j,m,1)c[i][j]=c[i][j+1]*131+c[i-1][j]*13331-c[i-1][j+1]*131*13331+num[i][j];
		//偶数边长
		F(i,1,n-1){
			F(j,1,m-1){
				int l=1,r=min(min(n-i,i),min(j,m-j)),ans=0;
				while(l<=r){
					int mid=(l+r)>>1;
					get(i-mid+1,j-mid+1,i+mid,j+mid);
					if(q.a==q.b&&q.a==q.c)l=mid+1,ans=mid;
					else r=mid-1;
				}tot+=ans;
			}
		}
		//奇数边长
		F(i,1,n){
			F(j,1,m){
				int l=1,r=min(min(n-i+1,i),min(m-j+1,j)),ans=0;
				while(l<=r){
					int mid=(l+r)>>1;
					get(i-mid+1,j-mid+1,i+mid-1,j+mid-1);
					if(q.a==q.b&&q.a==q.c)l=mid+1,ans=mid;
					else r=mid-1;
				}tot+=ans;
			}
		}
		pi(tot);
		return 0;
	}
}
signed main(){return EMT::main();}
posted @ 2021-07-10 11:15  letitdown  阅读(59)  评论(0编辑  收藏  举报