【题目描述】
给定数字矩阵,求矩阵中 上下对称 且 左右对称 的 正方形子矩阵 的 个数。
【题目链接】
BZOJ 1414 对称的正方形 【ZJOI 2009】
【解题思路】
二维的回文串?
似乎正解是 Manacher
,但我太弱啦没有看懂,所以就写了 HASH + 二分 的方法,卡一卡还是能过的。
先说一维的 HASH 做法吧,同 Manacher
一样,先在相邻的数之间加上某个没有出现过的数,以统一奇偶,然后预处理前缀哈希,预处理后缀哈希,我们可以 $O(1)$ 判断一个子串是不是回文的,只需要比较这个子串两个哈希的值就可以了。
然后我们枚举子串的中心点,然后二分长度,用哈希检验就可以了。
二分的依据就在于,对于同一个中心点,若某个长度的子串是回文的,那么长度比其短的一定是回文的,这很显然吧。
二维的情形是类似的,先在中间加数,然后预处理出以四个角为起点的哈希,比较四个哈希就可以判断。然后枚举正方形的中心点,二分边长,就解决了。
复杂度 $O(n^2\log n)$,常数较大。
【AC代码】
写起来略麻烦。
#include <cstdio>
#include <iostream>
#include <algorithm>
namespace IO{
const int IN_SIZE = 1 << 15;
char inBuf[IN_SIZE];
char *S, *T, ch;
inline char getchar(){
if(S == T) S = inBuf, T = inBuf + fread(inBuf, 1, IN_SIZE, stdin);
if(S == T) return EOF;
return *S++;
}
inline void readint(int &x){
do ch = getchar(); while(ch < '0' || ch > '9');
for(x = 0; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + (ch ^ '0');
}
}
const int MAXN = 1000;
const int MAXM = 1000;
const int REALN = 2 * MAXN + 1;
const int REALM = 2 * MAXM + 1;
typedef unsigned int HashInt;
const HashInt BASEX = 5261;
const HashInt BASEY = 1205;
HashInt powX[REALN + 1], powY[REALM + 1];
HashInt f[4][REALN + 1 + 1][REALN + 1 + 1];
int a[REALN + 1][REALM + 1];
int n, m;
inline void initHash(){
powX[0] = 1;
for(int i = 1; i <= n; i++) powX[i] = powX[i - 1] * BASEX;
powY[0] = 1;
for(int i = 1; i <= m; i++) powY[i] = powY[i - 1] * BASEY;
for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) f[0][i][j] = f[1][i][j] = f[2][i][j] = f[3][i][j] = a[i][j];
for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) f[0][i][j] += f[0][i][j - 1] * BASEX;
for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) f[0][i][j] += f[0][i - 1][j] * BASEY;
for(int i = 1; i <= n; i++) for(int j = m; j >= 1; j--) f[1][i][j] += f[1][i][j + 1] * BASEX;
for(int i = 1; i <= n; i++) for(int j = m; j >= 1; j--) f[1][i][j] += f[1][i - 1][j] * BASEY;
for(int i = n; i >= 1; i--) for(int j = 1; j <= m; j++) f[2][i][j] += f[2][i][j - 1] * BASEX;
for(int i = n; i >= 1; i--) for(int j = 1; j <= m; j++) f[2][i][j] += f[2][i + 1][j] * BASEY;
for(int i = n; i >= 1; i--) for(int j = m; j >= 1; j--) f[3][i][j] += f[3][i][j + 1] * BASEX;
for(int i = n; i >= 1; i--) for(int j = m; j >= 1; j--) f[3][i][j] += f[3][i + 1][j] * BASEY;
}
inline HashInt hash(int x1, int y1, int x2, int y2, int d){
HashInt powX = ::powX[x2 - x1 + 1], powY = ::powY[y2 - y1 + 1];
if(d == 0){
return f[0][x2][y2] - f[0][x1 - 1][y2] * powY - f[0][x2][y1 - 1] * powX + f[0][x1 - 1][y1 - 1] * powX * powY;
} else if(d == 1){
return f[1][x2][y1] - f[1][x1 - 1][y1] * powY - f[1][x2][y2 + 1] * powX + f[1][x1 - 1][y2 + 1] * powX * powY;
} else if(d == 2){
return f[2][x1][y2] - f[2][x2 + 1][y2] * powY - f[2][x1][y1 - 1] * powX + f[2][x2 + 1][y1 - 1] * powX * powY;
} else if(d == 3){
return f[3][x1][y1] - f[3][x2 + 1][y1] * powY - f[3][x1][y2 + 1] * powX + f[3][x2 + 1][y2 + 1] * powX * powY;
} else{
throw;
}
}
inline bool check(int x1, int y1, int x2, int y2){
HashInt std = hash(x1, y1, x2, y2, 0);
return (
std == hash(x1, y1, x2, y2, 1) &&
std == hash(x1, y1, x2, y2, 2) &&
std == hash(x1, y1, x2, y2, 3)
);
}
inline int solve(int x, int y){
int l = 1, r = std::min(std::min(x, n - x + 1), std::min(y, m - y + 1)), ans = 0;
while(l <= r){
int mid = l + r >> 1;
if(check(x - mid + 1, y - mid + 1, x + mid - 1, y + mid - 1)) ans = mid, l = mid + 1; else r = mid - 1;
}
return ans;
}
inline int solve(){
initHash();
int ans = 0;
for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) if((i + j & 1) == 0){
ans += solve(i, j) >> 1;
}
return ans;
}
int main(){
IO::readint(n), IO::readint(m);
n = 2 * n + 1, m = 2 * m + 1;
for(int i = 2; i <= n; i += 2) for(int j = 2; j <= m; j += 2) IO::readint(a[i][j]);
printf("%d\n", solve());
return 0;
}
就是这样啦