bzoj 1414: [ZJOI2009]对称的正方形

5 篇文章 0 订阅

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值