zoukankan      html  css  js  c++  java
  • [ZJOI2009]对称的正方形 manacher+单调队列

    快看,这里有个蒟蒻不会写单调队列!

    弱者就是弱。啥思路没有,码力还不行。

    显然的思路是考虑每一个中心点,求最大边长。发现每个点的限制挺难维护的。其实可以把限制拆开,拆成上下左右四个方向,最后求个min。

    以向上的方向为例。我们想求向上能扩展的最大边长。从上往下扫,维护半径单调递增的单调队列,每次从队头弹掉半径过小的点,记录最后一个弹掉的队头。然后边长就很好求了。

    然后我就写不出来了

    #include<bits/stdc++.h>
    using namespace std;
    typedef pair<int,int> pii;
    #define forg(i,x) for(int i=fir[x];i;i=nxt[i])
    #define uu unsigned
    #define scanf a1234=scanf
    #define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
    #define rint register int
    int a1234;
    char buf[1<<18],*bufs=buf,*buft=buf;
    inline int gc(){
        return bufs==buft&&(buft=(bufs=buf)+fread(buf,1,1<<18,stdin)),bufs==buft?-1:*bufs++;
    }
    inline void xxx(){for(;;);}
    inline int rd(int l,int r){return rand()%(r-l+1)+l;}
    
    const int mxn=2005;
    int a[mxn][mxn],ra[mxn],r1[mxn][mxn],r2[mxn][mxn];
    int n,m;
    inline void man1(rint x){
    //横向
        rint md=-1,rp=-1;
        for(rint i=1;i<=m;++i){
            if(i<rp)ra[i]=min(rp-i,ra[md*2-i]);
            else ra[i]=0;
            while(a[x][i+ra[i]]==a[x][i-ra[i]])++ra[i];
            --ra[i];
            if(i+ra[i]>rp)rp=i+ra[i],md=i;
        }
    }
    inline void man2(rint y){
    //纵向
        rint md=-1,rp=-1;
        for(rint i=1;i<=n;++i){
            if(i<rp)ra[i]=min(rp-i,ra[md*2-i]);
            else ra[i]=0;
            while(a[i+ra[i]][y]==a[i-ra[i]][y])++ra[i];
            --ra[i];
            if(i+ra[i]>rp)rp=i+ra[i],md=i;
        }
    }
    int lm[mxn][mxn];
    int q[mxn];
    inline void work1(){
        rint qh,qt,lim;
        for(rint j=2;j<m;++j){
            qh=1,qt=0;lim=0;
            for(rint i=1;i<=n;++i){
                while(qh<=qt&&r1[i][j]<=r1[q[qt]][j])--qt;
                q[++qt]=i;
                while(qh<=qt&&r1[q[qh]][j]<i-q[qh]+1)lim=q[qh],++qh;
                if(qh<=qt)lm[i][j]=min(i-lim-1,r1[q[qh]][j]);
            }
        }
    }
    inline void work2(){
        rint qh,qt,lim;
        for(rint j=2;j<m;++j){
            qh=1,qt=0;lim=n+1;
            for(rint i=n;i;--i){
                while(qh<=qt&&r1[i][j]<=r1[q[qt]][j])--qt;
                q[++qt]=i;
                while(qh<=qt&&r1[q[qh]][j]<q[qh]-i+1)lim=q[qh],++qh;
                if(qh<=qt)lm[i][j]=min(lm[i][j],min(lim-i-1,r1[q[qh]][j]));
            }
        }
    }
    inline void work3(){
        rint qh,qt,lim;
        for(rint i=2;i<n;++i){
            qh=1,qt=0; lim=0;
            for(rint j=1;j<=m;++j){
                while(qh<=qt&&r2[i][j]<=r2[i][q[qt]])--qt;
                q[++qt]=j;
                while(qh<=qt&&r2[i][q[qh]]<j-q[qh]+1)lim=q[qh],++qh;
                if(qh<=qt)lm[i][j]=min(lm[i][j],min(j-lim-1,r2[i][q[qh]]));
            }
        }
    }
    inline void work4(){
        rint qh,qt,lim;
        for(rint i=2;i<n;++i){
            qh=1,qt=0; lim=m+1;
            for(rint j=m;j;--j){
                while(qh<=qt&&r2[i][j]<=r2[i][q[qt]])--qt;
                q[++qt]=j;
                
                while(qh<=qt&&r2[i][q[qh]]<q[qh]-j+1)lim=q[qh],++qh;
                if(qh<=qt)lm[i][j]=min(lm[i][j],min(lim-j-1,r2[i][q[qh]]));
            }
        }
    }
    
    
    int main(){
        scanf("%d%d",&n,&m);
        n=n*2+1,m=m*2+1;
        for(int i=2;i<=n;i+=2)for(int j=2;j<=m;j+=2)scanf("%d",&a[i][j]),assert(a[i][j]);
        for(int i=0;i<=n;++i)a[i][0]=-1;for(int j=0;j<=m;++j)a[0][j]=-1;
        for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)if(!a[i][j])a[i][j]=-2;
        
        for(int i=1;i<=n;++i){
            man1(i);
            for(int j=1;j<=m;++j)r1[i][j]=ra[j];
        }
        for(int j=1;j<=m;++j){
            man2(j);
            for(int i=1;i<=n;++i)r2[i][j]=ra[i];
        }
        work1(),work2(),work3(),work4();
    //    for(int i=1;i<=n;++i,puts(""))for(int j=1;j<=m;++j)printf("%d ",lim1[i][j]);puts("");
        rint ans=0;
        for(int i=2;i<=n;i+=2)for(int j=2;j<=m;j+=2)ans+=(lm[i][j]+1)>>1;
        for(int i=1;i<=n;i+=2)for(int j=1;j<=m;j+=2)ans+=lm[i][j]>>1;
        printf("%d
    ",ans);
        return 0;
    }
    
    

    说几个比较重要的细节吧。

    还是以向上的方向为例(work1),如果中心行是(i)行,上界是第(x)行 (上界一定是关键行,但是第i行可以是辅助行),边长就是(i-x+1) 分类讨论一下挺好推的

    还有每次更新lim的时候其实应该特判关键行,但是不判也没事。

  • 相关阅读:
    尺取法 C
    并查集
    欧拉路与欧拉回路
    C
    最大连续区间和算法总结
    C
    python中的random函数方法
    Python可视化
    MFC学习之模态对话框和非模态对话框
    dropna
  • 原文地址:https://www.cnblogs.com/happyguy/p/14284246.html
Copyright © 2011-2022 走看看