【bzoj 1414】对称的正方形 单调队列+manacher
2017-09-17 11:52
316 查看
Description
Orez很喜欢搜集一些神秘的数据,并经常把它们排成一个矩阵进行研究。最近,Orez又得到了一些数据,并已经把它们排成了一个n行m列的矩阵。通过观察,Orez发现这些数据蕴涵了一个奇特的数,就是矩阵中上下对称且左右对称的正方形子矩阵的个数。 Orez自然很想知道这个数是多少,可是矩阵太大,无法去数。只能请你编个程序来计算出这个数。Input
文件的第一行为两个整数n和m。接下来n行每行包含m个正整数,表示Orez得到的矩阵。Output
文件中仅包含一个整数answer,表示矩阵中有answer个上下左右对称的正方形子矩阵。Sample Input
5 54 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
题解:
蒟蒻写了4h……(本来是想怂,但看到人家说gang了一晚上,然后默默关了网页自己去作了),还有,膜bzoj 1414榜上900B+400MS大佬。首先用manacher,双倍复制原数组,跑出$P_{0,i,j},P_{1,i,j}$,分别表示第i行j列的横着的和竖着的回文半径。
显然只要求出每个位置的最大正方形边长答案就出来了。
我们以每个位置$(i,j)$为坐标轴原点,显然,我们只要得到x,y轴上的回文半径即可。先讨论x非负轴。同时,对于每个位置我们可以观察发现,在x轴上的位置,应该满足其$x-p[1][i][x]+1<=j$。然后发现对于$(i,j+1)$是可以继承满足$(i,j)$的一部分点,而不能继承的只有$(i,j)$在x轴对应点,同时我们可能会有一部分新点加入$(i,j+1)$的集合点。(⊙v⊙)嗯,这不就是队列的时间关系嘛。
然后怎么选取$(i,j)$所能得到的此时尽可能最大值边长呢。我们可以画个图,观察发现,我们在$(i,j)$点集的选取,只和最小值有关,所以当出现第一个不满足$x-p_{1,i,x}+1<=j$的点就没必要再在非负半轴上往后扫了。
证明的话倒是挺简单的,就不多说了。
以上一结合就得到了我们需要的数据结构,单调队列。
那么对于x非正半轴以及y轴的情况也与x非负半轴的情况相同。时间复杂度$O(n^{2})$
最后答案累加每个$(i,j)$奇偶性相同的位置即可。
Ps:可能是我打得蠢……都跑不过带$log$的……
代码:
#include<cstdio> #include<iostream> #include<cstring> using namespace std; inline int read(){ int s=0;char ch=getchar(); while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9') s=s*10+(ch^48),ch=getchar(); return s; } int n,m; int Mar[2010][2010]; int p[2][2010][2010]; inline void manacher(){ for(int i=1;i<=2*n+1;i++){ int pos,mar=0; for(int j=1;j<=2*m+1;j++){ if(mar>j) p[0][i][j]=min(p[0][i][pos*2-j],mar-j-1); else p[0][i][j]=1; while(Mar[i][j-p[0][i][j]]==Mar[i][j+p[0][i][j]]) p[0][i][j]++; if(mar<j+p[0][i][j]-1) mar=j+p[0][i][j]-1,pos=j; } } for(int i=1;i<=2*m+1;i++){ int pos,mar=0; for(int j=1;j<=2*n+1;j++){ if(mar>j) p[1][i][j]=min(p[1][i][pos*2-j],mar-j-1); else p[1][i][j]=1; while(Mar[j-p[1][i][j]][i]==Mar[j+p[1][i][j]][i]) p[1][i][j]++; if(mar<j+p[1][i][j]-1) mar=j+p[1][i][j]-1,pos=j; } } } int que[2010],l,r; int re[2010][2010]; int main(){ n=read(),m=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) Mar[i<<1][j<<1]=read(); for(int i=1;i<=2*n+1;i++) Mar[i][0]=-2,Mar[i][m+1<<1]=-1; for(int i=1;i<=2*m+1;i++) Mar[0][i]=-2,Mar[n+1<<1][i]=-1; manacher(); for(int i=2;i<=2*n;i++){ l=1,r=0; for(int j=((i^1)&1)+1,k=1;j<=2*m+1;j+=2){ while(k<=2*m+1&&k-p[1][k][i]+1<=j){ while(l<=r&&p[1][que[r]][i]>=p[1][k][i]) r--; que[++r]=k; k++; } while(l<=r&&que[l]<j) l++; re[i][j]=min(que[r]-j+1,p[1][que[l]][i]); } l=1,r=0; for(int j=2*m+1-((i^1)&1),k=2*m+1;j>=0;j-=2){ while(k&&k+p[1][k][i]-1>=j){ while(l<=r&&p[1][que[r]][i]>=p[1][k][i]) r--; que[++r]=k--; } while(l<=r&&que[l]>j) l++; re[i][j]=min(min(j-que[r]+1,p[1][que[l]][i]),re[i][j]); } } for(int i=2;i<=2*m;i++){ l=1,r=0; for(int j=1+((i^1)&1),k=1;j<=2*n+1;j+=2){ while(k<=2*n+1&&k-p[0][k][i]+1<=j){ while(l<=r&&p[0][que[r]][i]>=p[0][k][i]) r--; que[++r]=k; k++; } while(l<=r&&que[l]<j) l++; re[j][i]=min(min(que[r]-j+1,p[0][que[l]][i]),re[j][i]); } l=1,r=0; for(int j=2*n+1-((i^1)&1),k=2*n+1;j;j--){ while(k&&k+p[0][k][i]-1>=j){ while(l<=r&&p[0][que[r]][i]>=p[0][k][i]) r--; que[++r]=k--; } while(l<=r&&que[l]>j) l++; re[j][i]=min(min(j-que[r]+1,p[0][que[l]][i]),re[j][i]); } } int ans=0; for(int i=2;i<=2*n;i++){ for(int j=((i^1)&1)+1;j<=2*m+1;j+=2) if((i&1)==(j&1)) ans+=re[i][j]>>1; } printf("%d",ans); }
相关文章推荐
- bzoj 1414 对称的正方形
- bzoj 1414: [ZJOI2009]对称的正方形 manacher算法+單調隊列
- BZOJ 1047 [HAOI2007]理想的正方形【单调队列
- bzoj 1047: [HAOI2007]理想的正方形 (单调队列)
- bzoj1414 [ZJOI2009]对称的正方形 && bzoj3705 对称的正方形
- bzoj 1047: [HAOI2007]理想的正方形【单调队列】
- BZOJ 1047 HAOI2007 理想的正方形 单调队列
- bzoj 1047 : [HAOI2007]理想的正方形 单调队列dp
- 【二维单调队列】BZOJ1047-[HAOI2007]理想的正方形
- [BZOJ1047][HAOI2007]理想的正方形(单调队列)
- BZOJ 1047: [HAOI2007]理想的正方形( 单调队列 )
- [BZOJ]1414: [ZJOI2009]对称的正方形 二分+hash
- bzoj1047[HAOI2007]理想的正方形 单调队列
- 【BZOJ】【P1047】【HAOI2007】【理想的正方形】【题解】【单调队列】
- 【bzoj1047】【单调队列】【HAOI2007】理想的正方形
- bzoj1414 [ZJOI2009]对称的正方形(二分答案+二维哈希)
- [BZOJ1414][ZJOI2009]对称的正方形(manacher+单调栈+二分)
- bzoj1047: [HAOI2007]理想的正方形(单调队列)
- [BZOJ 1047] [HAOI2007] 理想的正方形 【单调队列】
- 【单调队列】BZOJ1047(HAOI2007)[理想的正方形]题解