BZOJ1414: [ZJOI2009]对称的正方形##
Time Limit: 10 Sec
Memory Limit: 162 MBDescription###
Orez很喜欢搜集一些神秘的数据,并经常把它们排成一个矩阵进行研究。最近,Orez又得到了一些数据,并已经把它们排成了一个n行m列的矩阵。通过观察,Orez发现这些数据蕴涵了一个奇特的数,就是矩阵中上下对称且左右对称的正方形子矩阵的个数。 Orez自然很想知道这个数是多少,可是矩阵太大,无法去数。只能请你编个程序来计算出这个数。
Input###
文件的第一行为两个整数n和m。接下来n行每行包含m个正整数,表示Orez得到的矩阵。
Output###
文件中仅包含一个整数answer,表示矩阵中有answer个上下左右对称的正方形子矩阵。
Sample Input###
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
Sample Output###
27
HINT
数据范围
对于30%的数据 n,m≤100
对于100%的数据 n,m≤1000 ,矩阵中的数的大小≤10^9
题目地址:BZOJ1414: [ZJOI2009]对称的正方形
题目大意: 题目很简洁了>_<
题解:
据说正解是Manacher
然而二维 hash 可以过
现在中间塞 0
样例变成:
00000000000
04020404040
00000000000
03010404030
00000000000
03050303030
00000000000
03010503030
00000000000
04020102040
00000000000
然后只要每次寻找长度为奇数的正方形就可以了
每个对称的正方形上下一样左右一样,只枚举i+j为偶数的点 得到的边长除以2就是以这个点为中心的正方形回文子矩阵数量
只要做三个二维 hash 就可以了
(三个方向,从左上到右下,右上到左下,左下到右上 就够了不用四个方向)
对于一个找到的正方形只要他在这三个 hash 矩阵中值一样,就是对称的正方形
然后发现如果枚举正方形的中心点,长度有二分性
每次二分 judge 一下就可以了
ubuntu 输入法没有记忆功能,难受的一匹
AC代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#define uint unsigned int
using namespace std;
const int N=2e3+5,B1=1e5+7,B2=1e5+9;
int n,m,ans;
uint p1[N],p2[N],a[N][N],h1[N][N],h2[N][N],h3[N][N];
inline int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x;
}
bool judge(int x,int y,int l){
int len=(l<<1)-1;
uint A,B;
A=h1[x+l-1][y+l-1]
-h1[x-l][y+l-1]*p1[len]
-h1[x+l-1][y-l]*p2[len]
+h1[x-l][y-l]*p1[len]*p2[len];
B=h2[x+l-1][y-l+1]
-h2[x-l][y-l+1]*p1[len]
-h2[x+l-1][y+l]*p2[len]
+h2[x-l][y+l]*p1[len]*p2[len];
if(A!=B)return 0;
B=h3[x-l+1][y+l-1]
-h3[x+l][y+l-1]*p1[len]
-h3[x-l+1][y-l]*p2[len]
+h3[x+l][y-l]*p1[len]*p2[len];
if(A!=B)return 0;
return 1;
}
int erfen(int x,int y){
int l=1,r=min(min(x,n-x+1),min(y,m-y+1)),res=0;
while(l<=r){
int mid=(l+r)>>1;
if(judge(x,y,mid)){
res=mid;
l=mid+1;
}else r=mid-1;
}
return res;
}
int main(){
n=read()<<1|1,m=read()<<1|1;
for(int i=2;i<=n;i+=2)
for(int j=2;j<=m;j+=2)
a[i][j]=read();
p1[0]=1;for(int i=1;i<=n;i++)p1[i]=p1[i-1]*B1;
p2[0]=1;for(int i=1;i<=m;i++)p2[i]=p2[i-1]*B2;
memcpy(h1,a,sizeof(a));
memcpy(h2,a,sizeof(a));
memcpy(h3,a,sizeof(a));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
h1[i][j]+=h1[i-1][j]*B1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
h1[i][j]+=h1[i][j-1]*B2;
for(int i=1;i<=n;i++)
for(int j=m;j>=1;j--)
h2[i][j]+=h2[i-1][j]*B1;
for(int i=1;i<=n;i++)
for(int j=m;j>=1;j--)
h2[i][j]+=h2[i][j+1]*B2;
for(int i=n;i>=1;i--)
for(int j=1;j<=m;j++)
h3[i][j]+=h3[i+1][j]*B1;
for(int i=n;i>=1;i--)
for(int j=1;j<=m;j++)
h3[i][j]+=h3[i][j-1]*B2;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(((i^j^1)&1))ans+=(erfen(i,j)>>1);
printf("%d
",ans);
return 0;
}