【bzoj 1414】对称的正方形 单调队列+manacher

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

数据范围
对于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$的……

代码:

  

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cstring>
  4 using namespace std;
  5 inline int read(){
  6     int s=0;char ch=getchar();
  7     while(ch<‘0‘||ch>‘9‘)   ch=getchar();
  8     while(ch>=‘0‘&&ch<=‘9‘) s=s*10+(ch^48),ch=getchar();
  9     return s;
 10 }
 11 int n,m;
 12 int Mar[2010][2010];
 13 int p[2][2010][2010];
 14 inline void manacher(){
 15     for(int i=1;i<=2*n+1;i++){
 16         int pos,mar=0;
 17         for(int j=1;j<=2*m+1;j++){
 18             if(mar>j)   p[0][i][j]=min(p[0][i][pos*2-j],mar-j-1);
 19             else    p[0][i][j]=1;
 20             while(Mar[i][j-p[0][i][j]]==Mar[i][j+p[0][i][j]])   p[0][i][j]++;
 21             if(mar<j+p[0][i][j]-1)
 22                 mar=j+p[0][i][j]-1,pos=j;
 23         }
 24     }
 25     for(int i=1;i<=2*m+1;i++){
 26         int pos,mar=0;
 27         for(int j=1;j<=2*n+1;j++){
 28             if(mar>j)   p[1][i][j]=min(p[1][i][pos*2-j],mar-j-1);
 29             else    p[1][i][j]=1;
 30             while(Mar[j-p[1][i][j]][i]==Mar[j+p[1][i][j]][i])   p[1][i][j]++;
 31             if(mar<j+p[1][i][j]-1)
 32                 mar=j+p[1][i][j]-1,pos=j;
 33         }
 34     }
 35 }
 36 int que[2010],l,r;
 37 int re[2010][2010];
 38 int main(){
 39     n=read(),m=read();
 40     for(int i=1;i<=n;i++)
 41         for(int j=1;j<=m;j++)
 42             Mar[i<<1][j<<1]=read();
 43     for(int i=1;i<=2*n+1;i++)
 44         Mar[i][0]=-2,Mar[i][m+1<<1]=-1;
 45     for(int i=1;i<=2*m+1;i++)
 46         Mar[0][i]=-2,Mar[n+1<<1][i]=-1;
 47     manacher();
 48     for(int i=2;i<=2*n;i++){
 49         l=1,r=0;
 50         for(int j=((i^1)&1)+1,k=1;j<=2*m+1;j+=2){
 51             while(k<=2*m+1&&k-p[1][k][i]+1<=j){
 52                 while(l<=r&&p[1][que[r]][i]>=p[1][k][i])
 53                     r--;
 54                 que[++r]=k;
 55                 k++;
 56             }
 57             while(l<=r&&que[l]<j)
 58                 l++;
 59             re[i][j]=min(que[r]-j+1,p[1][que[l]][i]);
 60         }
 61         l=1,r=0;
 62         for(int j=2*m+1-((i^1)&1),k=2*m+1;j>=0;j-=2){
 63             while(k&&k+p[1][k][i]-1>=j){
 64                 while(l<=r&&p[1][que[r]][i]>=p[1][k][i])
 65                     r--;
 66                 que[++r]=k--;
 67             }
 68             while(l<=r&&que[l]>j)
 69                 l++;
 70             re[i][j]=min(min(j-que[r]+1,p[1][que[l]][i]),re[i][j]);
 71         }
 72
 73     }
 74     for(int i=2;i<=2*m;i++){
 75         l=1,r=0;
 76         for(int j=1+((i^1)&1),k=1;j<=2*n+1;j+=2){
 77             while(k<=2*n+1&&k-p[0][k][i]+1<=j){
 78                 while(l<=r&&p[0][que[r]][i]>=p[0][k][i])
 79                     r--;
 80                 que[++r]=k;
 81                 k++;
 82             }
 83             while(l<=r&&que[l]<j)
 84                 l++;
 85             re[j][i]=min(min(que[r]-j+1,p[0][que[l]][i]),re[j][i]);
 86         }
 87         l=1,r=0;
 88         for(int j=2*n+1-((i^1)&1),k=2*n+1;j;j--){
 89             while(k&&k+p[0][k][i]-1>=j){
 90                 while(l<=r&&p[0][que[r]][i]>=p[0][k][i])
 91                     r--;
 92                 que[++r]=k--;
 93             }
 94             while(l<=r&&que[l]>j)
 95                 l++;
 96             re[j][i]=min(min(j-que[r]+1,p[0][que[l]][i]),re[j][i]);
 97         }
 98     }
 99     int ans=0;
100     for(int i=2;i<=2*n;i++){
101         for(int j=((i^1)&1)+1;j<=2*m+1;j+=2)
102             if((i&1)==(j&1))
103                 ans+=re[i][j]>>1;
104     }
105     printf("%d",ans);
106 }
时间: 2024-10-14 13:07:32

【bzoj 1414】对称的正方形 单调队列+manacher的相关文章

BZOJ 1047 理想的正方形(单调队列)

刚开始用二维RMQ直接给超内存了... 用单调队列可以做到O(n^2)的复杂度.具体是先把每行用单调队列处理一下.再把处理后的用列单调队列处理下. # include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # in

BZOJ 1047 HAOI 2007 理想的正方形 单调队列

题目大意:给出一个矩阵,求出一个k*k的子矩阵,使得这个矩阵中最大值和最小值的差最小,输出这个差值. 思路:利用单调队列维护每一行的数字,求出一个数字前面k个数字中的最大值和最小值,然后在列上暴力求出真个矩阵的最大值和最小值,总时间复杂度O(M*M+M*M*K). CODE: #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algor

洛谷OJ 2216 理想的正方形 单调队列(二维)

https://www.luogu.org/problem/show?pid=2216 题意:给出a*b矩形 从中找到一个n*n正方形,其(最大值-最小值之差)最小,a,b<=1e3,n<=100暴力枚举正方形右下角,如何快速算出其最大值和最小值?先用单调队列预处理出ma[i][j] 表示(i,j)以第i行j列结尾长度为n的最大值/在枚举列之后,对同一个列,由于已经知道该列 每行长度为n的最值 则在次利用单调队列,从上往下扫描行,求出(i,j)为右下角的矩形的最值即可 #include <

HAOI2007 理想的正方形 单调队列

单调队列 by   GeneralLiu 滑动窗口是比较裸的单调队列 理想的正方形   就拔高了一个层次(多了一维) 有一个a*b的整数组成的矩阵 现请你从中找出一个n*n的正方形区域 使得该区域所有数中的最大值和最小值的差最小 只写MAX了,MIN一个道理,懒 不写了 先横着跑单调队列 维护许多长度为 n 的 横着的MAX 存在数组 map1[][] 中 再对数组 map1[][] 竖着跑单调队列 就维护了 许多 n*n 的 矩阵的MAX MIN同理 在竖着跑单调队列时 顺便MAX 与 MIN

P2216 [HAOI2007]理想的正方形 - 单调队列

这种矩形问题常用单调队列优化枚举(通过贪心取最优降低了一个维度的枚举) 推荐这道题也要做一做:[ZJOI2007]棋盘制作 单调队列的空间记得开大点! 反正内存够用 注意,这题正方形边长是固定的! 暴力算法是枚举上边界所在的行,左边界所在的列,通过这两个个信息确定一个正方形,然后预处理出一行中从第i个点到i+n个点的最值,再扫一遍这个正方形的行,复杂度是N^3 预处理的时候,复杂度也是N^3...考虑用单调队列来维护,枚举到一行,看做一个序列,求长度为n的区间最值,就是一个滑动窗口,如果不大明白

BZOJ 1791 岛屿(环套树+单调队列DP)

题目实际上是求环套树森林中每个环套树的直径. 对于环套树的直径,可以先找到这个环套树上面的环.然后把环上的每一点都到达的外向树上的最远距离作为这个点的权值. 那么直径一定就是从环上的某个点开始,某个点结束的. 把环拆成链,定义dp[i]表示第i个点为结束点的最远距离,显然有dp[i]=val[j]+sum[i]-sum[j-1]+val[i].显然可以用单调队列优化这个DP. 剩下的就是这样依次统计每个环套树的直径之和. 对于环套树上找环可以借鉴最小树形图找环的技巧. 首先将边定向,保证每个点的

BZOJ 1012 线段树或单调队列

1012: [JSOI2008]最大数maxnumber 题意:两种操作:1.查询当前数列中末尾L个数中的最大的数:2.当前数列末尾插入一个数. tags:水题 线段树 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define FF(i,a,b) for (int i=a;i<=b;i++) #define F(i

[BZOJ1047] [HAOI2007] 理想的正方形 (单调队列)

Description 有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小. Input 第一行为3个整数,分别表示a,b,n的值第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数.每行相邻两数之间用一空格分隔.100%的数据2<=a,b<=1000,n<=a,n<=b,n<=100 Output 仅一个整数,为a*b矩阵中所有“n*n正方形区域中的最大整数和最小整数的差值”的最小值. Sample In

bzoj 3126: [Usaco2013 Open]Photo——单调队列优化dp

Description 给你一个n长度的数轴和m个区间,每个区间里有且仅有一个点,问能有多少个点 Input * Line 1: Two integers N and M. * Lines 2..M+1: Line i+1 contains a_i and b_i. Output * Line 1: The maximum possible number of spotted cows on FJ's farm, or -1 if there is no possible solution. S