BZOJ1414: [ZJOI2009]对称的正方形(二维hash)


BZOJ1414: [ZJOI2009]对称的正方形##

  Time Limit: 10 Sec
  Memory Limit: 162 MB

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
    

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\n",ans);
    return 0;
}
posted @ 2018-09-11 20:25  skl_win  阅读(434)  评论(0编辑  收藏  举报
Live2D