Description
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
数据范围
对于30%的数据 n,m≤100
对于100%的数据 n,m≤1000 ,矩阵中的数的大小≤109
题解
感觉TYB说的很对啊
这种题唯一不好就是很难调。。静态查错了很久,发现是有一个地方m写成n了。。
CODE:
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
typedef unsigned long long LL;
const LL N=1005;
const LL MOD=10037;
const LL MOD1=2333;
LL pow1[N],pow2[N];
LL n,m;
LL a[N][N];
LL f[5][N][N];//从四个方向来的hash值
void prepare ()
{
/*左上角*/
for (LL u=1;u<=n;u++)
for (LL i=1;i<=m;i++)
f[0][u][i]+=f[0][u-1][i]*MOD+a[u][i];
for (LL u=1;u<=n;u++)
for (LL i=1;i<=m;i++)
f[0][u][i]+=f[0][u][i-1]*MOD1;
/*左上角*/
/*右上角*/
for (LL u=1;u<=n;u++)
for (LL i=m;i>=1;i--)
f[1][u][i]+=f[1][u-1][i]*MOD+a[u][i];
for (LL u=1;u<=n;u++)
for (LL i=m;i>=1;i--)
f[1][u][i]+=f[1][u][i+1]*MOD1;
/*右上角*/
/*左下角*/
for (LL u=n;u>=1;u--)
for (LL i=1;i<=m;i++)
f[2][u][i]+=f[2][u+1][i]*MOD+a[u][i];
for (LL u=n;u>=1;u--)
for (LL i=1;i<=m;i++)
f[2][u][i]+=f[2][u][i-1]*MOD1;
/*左下角*/
for (LL u=n;u>=1;u--)
for (LL i=m;i>=1;i--)
f[3][u][i]+=f[3][u+1][i]*MOD+a[u][i];
for (LL u=n;u>=1;u--)
for (LL i=m;i>=1;i--)
f[3][u][i]+=f[3][u][i+1]*MOD1;
/*右下角*/
}
LL get0 (LL x1,LL y1,LL x2,LL y2)
{
return f[0][x2][y2]-f[0][x1-1][y2]*pow1[x2-x1+1]-f[0][x2][y1-1]*pow2[y2-y1+1]+f[0][x1-1][y1-1]*pow1[x2-x1+1]*pow2[y2-y1+1];
}
LL get1 (LL x1,LL y1,LL x2,LL y2)
{
return f[1][x2][y2]-f[1][x1-1][y2]*pow1[x2-x1+1]-f[1][x2][y1+1]*pow2[y1-y2+1]+f[1][x1-1][y1+1]*pow1[x2-x1+1]*pow2[y1-y2+1];
}
LL get2 (LL x1,LL y1,LL x2,LL y2)
{
return f[2][x2][y2]-f[2][x1+1][y2]*pow1[x1-x2+1]-f[2][x2][y1-1]*pow2[y2-y1+1]+f[2][x1+1][y1-1]*pow2[y2-y1+1]*pow1[x1-x2+1];
}
LL get3 (LL x1,LL y1,LL x2,LL y2)
{
return f[3][x2][y2]-f[3][x1+1][y2]*pow1[x1-x2+1]-f[3][x2][y1+1]*pow2[y1-y2+1]+f[3][x1+1][y1+1]*pow1[x1-x2+1]*pow2[y1-y2+1];
}
void solve ()
{
LL ans=0;
for (LL u=1;u<=n;u++)
for (LL i=1;i<=m;i++)//这个是真的中间点--->奇数
{
LL l=1,r=min(min(u,n-u+1),min(i,m-i+1));
while (l<=r)
{
LL mid=(l+r)>>1;
LL a=get0(u-mid+1,i-mid+1,u,i);
LL b=get1(u-mid+1,i+mid-1,u,i);
LL c=get2(u+mid-1,i-mid+1,u,i);
LL d=get3(u+mid-1,i+mid-1,u,i);
if (a==b&&b==c&&c==d) l=mid+1;
else r=mid-1;
}
ans=ans+l-1;
}
for (LL u=1;u<n;u++)
for (LL i=1;i<m;i++)//偶数
{
LL l=1,r=min(min(u,n-u),min(i,m-i));
while (l<=r)
{
LL mid=(l+r)>>1;
LL a=get0(u-mid+1,i-mid+1,u,i);
LL b=get1(u-mid+1,i+mid,u,i+1);
LL c=get2(u+mid,i-mid+1,u+1,i);
LL d=get3(u+mid,i+mid,u+1,i+1);
if (a==b&&b==c&&c==d) l=mid+1;
else r=mid-1;
}
ans=ans+l-1;
}
printf("%llu\n",ans);
}
int main()
{
scanf("%llu%llu",&n,&m);
for (LL u=1;u<=n;u++)
for (LL i=1;i<=m;i++)
scanf("%llu",&a[u][i]);
pow1[0]=pow2[0]=1;for (LL u=1;u<=1000;u++) pow1[u]=pow1[u-1]*MOD,pow2[u]=pow2[u-1]*MOD1;
prepare();
solve();
return 0;
}