bzoj1414: [ZJOI2009]对称的正方形&bzoj3705: 对称的正方形

题目
题解

首先我们考虑偶数个点和奇数个点的方阵枚举中心方式不太相同,我们用类似manacher的处理方法,填上一堆0,把他们全都变成奇数的情况。然后我们枚举每一个点作为中心,二分答案找到以这个点为中心最大的合法方阵。就可以直接统计这个点对答案的贡献了。这样已经是O(n^2logn)的了,我们需要O(1)判断一个方阵是否上下左右均对称。类似不用manacher求最长回文子串的方法,把这个子串镜像过来求最长公共子串,我们分别做出这个矩阵的上下镜面和左右镜面,然后每次就只需要判定这三个方阵区域是否相同。可以用二维hash预处理来O(1)判断。

温馨提醒:此题需卡常,inline和读入优化需要加上去(大佬例外,自带常数优化QAQ)

#include<cstdio>
#include<algorithm>
using namespace std;
typedef unsigned int U;
const int N=2002;
const U k1=1000003,k2=101;
U h[3][N][N],b1[N],b2[N];
int a[N][N],i,j,l,r,mid,n,m,ans;
inline char gc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
#define gc getchar
inline int read(){
    int x=0,fl=1;char ch=gc();
    for (;ch<48||ch>57;ch=gc())if(ch=='-')fl=-1;
    for (;48<=ch&&ch<=57;ch=gc())x=(x<<3)+(x<<1)+(ch^48);
    return x*fl;
}
inline U ha(int o,int x1,int y1,int x2,int y2){
    return h[o][x2][y2]+h[o][x1-1][y1-1]*b1[x2-x1+1]*b2[y2-y1+1]
    -h[o][x1-1][y2]*b1[x2-x1+1]-h[o][x2][y1-1]*b2[y2-y1+1];
}
inline bool check(int x,int y,int len){
    int x1=x-len+1,x2=x+len-1,y1=y-len+1,y2=y+len-1;
    U v1=ha(0,x1,y1,x2,y2),v2=ha(1,n-x2+1,y1,n-x1+1,y2),v3=ha(2,x1,m-y2+1,x2,m-y1+1);
    return v1==v2 && v1==v3;
}
int main(){
    n=read();m=read();
    for (i=1;i<=n;i++)
        for (j=1;j<=m;j++) a[i*2-1][j*2-1]=read();
    n=n*2-1;m=m*2-1;
    b1[0]=b2[0]=1;
    for (i=1;i<=n;i++) b1[i]=b1[i-1]*k1;
    for (i=1;i<=m;i++) b2[i]=b2[i-1]*k2;
    for (i=1;i<=n;i++)
        for (j=1;j<=m;j++)  h[0][i][j]=h[0][i][j-1]*k2+a[i][j],
                            h[1][i][j]=h[1][i][j-1]*k2+a[n-i+1][j],
                            h[2][i][j]=h[2][i][j-1]*k2+a[i][m-j+1];
    for (i=1;i<=n;i++)
        for (j=1;j<=m;j++)  h[0][i][j]+=h[0][i-1][j]*k1,
                            h[1][i][j]+=h[1][i-1][j]*k1,
                            h[2][i][j]+=h[2][i-1][j]*k1;
    for (i=1;i<=n;i++)
        for (j=1;j<=m;j++)
            if (!(i+j&1)){
                l=1;r=min(min(i,n-i+1),min(j,m-j+1));
                while (l<=r){
                    mid=l+r>>1;
                    if (check(i,j,mid)) l=mid+1;
                    else r=mid-1;
                }
                if (i&1) ans+=l>>1;
                else ans+=l-1>>1;
            }
    printf("%d",ans);
}

猜你喜欢

转载自blog.csdn.net/xumingyang0/article/details/80740300
今日推荐