bzoj1414 [ZJOI2009]对称的正方形

25 篇文章 0 订阅

http://www.elijahqi.win/2018/01/16/bzoj1414/
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

数据范围
6
对于30%的数据 n,m≤100

对于100%的数据 n,m≤1000 ,矩阵中的数的大小≤109
给定 一个矩阵 求子正方形的数量 满足上下对称且左右对称的个数是多少 那网上有的做法是manacher 其实还可以二分加hash来搞 怎么搞 因为我现在正方形存在奇数和偶数不方便所以我干脆像manacher一样先都预先增加一些点 然后都当奇数来做即可 怎么搞 枚举每个中心然后去二分我这个正方形的边长 然后hash验证一下我这个正方形是否满足条件 第一次搞二维hash怎么搞 就类似一纬的做就好 然后再增加一个类似进制一样 总共两个k1&k2用来处理行和列 关于这个hash怎么想 蒟蒻我是这么考虑的 我们来看 我十进制数是不是直接o(1)即可比较 为什么因为他们进制不同?那我可以把每个数都乘一个禁止 然后比如如何表示ab那么我可以看做一个十进制下的9798这个数 然后针对一个质数取模就可以分散到链表中了 题目中我hash1表示1-i 1-j这个前缀区间的hash值 然后针对1-j列1-i行的hash值我现在已经储存那么我相当于把这个前缀再乘进制数 然后加上我当前这个一行的即可
%%%zhx巨佬 让我对于hash有了更进一步的认识 对于每行每个数我其实可以看作 i=1xj=1yk1xk2ymp[i][j] 就是我这个点前缀的hash值 然后通过自然溢出取模 关于这个我相当于是一个hash的前缀和 那么我算的时候相当于我去减前面的hash值我就可以相当于把那个区间就取了出来

#include<cstdio>
#define N 2200
#define k1 1000003
#define k2 101
#include<algorithm>
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S) {T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0;char ch=gc();
    while(ch<'0'||ch>'9') ch=gc();
    while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=gc();
    return x;
}
int mp[N][N],n,m,ans;unsigned int hash1[N][N],hash2[N][N],hash3[N][N],base1[N*N],base2[N*N];
inline bool check(int l1,int r1,int l2,int r2){
    int x0=hash1[r1][r2]-hash1[r1][l2-1]*base2[r2-l2+1]-hash1[l1-1][r2]*base1[r1-l1+1]+hash1[l1-1][l2-1]*base2[r2-l2+1]*base1[r1-l1+1];
    int x1=hash2[r1][l2]-hash2[l1-1][l2]*base1[r1-l1+1]-hash2[r1][r2+1]*base2[r2-l2+1]+hash2[l1-1][r2+1]*base1[r1-l1+1]*base2[r2-l2+1];
    if (x0!=x1) return 0;
    int x2=hash3[l1][r2]-hash3[r1+1][r2]*base1[r1-l1+1]-hash3[l1][l2-1]*base2[r2-l2+1]+hash3[r1+1][l2-1]*base2[r2-l2+1]*base1[r1-l1+1];
    if (x0!=x2) return 0;
    return 1;
}
int main(){
    freopen("bzoj1414.in","r",stdin);
    n=read();m=read();
    for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) mp[(i<<1)-1][(j<<1)-1]=read();
    n<<=1;m<<=1;--n,--m;base1[0]=base2[0]=1;int max1=max(n,m);
    for (int i=1;i<=max1;++i) base1[i]=base1[i-1]*k1,base2[i]=base2[i-1]*k2;
    for (int i=1;i<=n;++i)
        for (int j=1;j<=m;++j) hash1[i][j]=hash1[i][j-1]*k2+mp[i][j];
    for (int i=1;i<=n;++i)
        for (int j=1;j<=m;++j) hash1[i][j]+=hash1[i-1][j]*k1;
    for (int i=1;i<=n;++i)
        for (int j=m;j;--j) hash2[i][j]=hash2[i][j+1]*k2+mp[i][j];
    for (int i=1;i<=n;++i)
        for (int j=m;j;--j) hash2[i][j]+=hash2[i-1][j]*k1;
    for (int i=n;i;--i)
        for (int j=1;j<=m;++j) hash3[i][j]=hash3[i][j-1]*k2+mp[i][j];
    for (int i=n;i;--i)
        for (int j=1;j<=m;++j) hash3[i][j]+=hash3[i+1][j]*k1;
    for (int i=1;i<=n;++i){
        for (int j=1;j<=m;++j){
            if ((i+j&1)==0){
                int l=1,r=min(min(i,j),min(n-i+1,m-j+1));
                while(l<=r){
                    int mid=l+r>>1;
                    if (check(i-mid+1,i+mid-1,j-mid+1,j+mid-1)) l=mid+1;else r=mid-1;
                }
                if (i&1) ans+=(r+1)>>1;else ans+=r>>1;  
            }
        }
    }printf("%d",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值