题目大意:
给一个N * M的正整数矩阵(N和M均不超过1000), 求上下对称且左右对称的正方形子矩阵个数.
简要分析:
晕...这难道是二维的回文串???
因为正方形长度可以是奇数也可以是偶数, 讨论很讨厌, 所以我们先把原来矩阵两两之间填上0, 变成(2 * N - 1) * (2 * M - 1)的矩阵.
这样我们就只用考虑正方形变长为奇数的情况, 也就是说我们需要统计以每个合法位置为中心的"回文"正方形个数.
于是我们可以求出一个中心(i, j), 在只考虑向左的方向时, left[i][j]记录它最长能延伸多远. 同理可以定义right[i][j], up[i][j], down[i][j]. 这样min(left[i][j], right[i][j], up[i][j], down[i][j]) * 2 + 1就是以(i, j) 为中心时的正方形边长, 这时按(i, j)是否为0分类讨论一下就可以统计答案了.
我们考虑计算left[i][j](其他几个和这个差不多), 计算这个我们需要另外一个值, col[j][i], 即在第j列上, 以第i行这个元素为中心的最长回文串长度除以2. 那么我们有
left[i][j] = min{k | v[k][j] >= j - k, k <= j}, v[k][j]表示col[k][i]到col[j][i]的最小值, v数组可以用ST算法实现.
直接求复杂度显然太高, 但注意到随k增大, v[k][j]不减, j - k递减, 于是可以通过某种二分计算出k的值. 很遗憾的是, 该算法超时了.
我们考虑left[i][j] = p, left[i][j + 1] = q, 有v[p][j] >= j - p, v[q][j + 1] >= (j + 1) - q, 若q < p, 则有v[p][j] >= v[q][j] >= v[q][j + 1] >= (j + 1) - q > j - q, 注意到不等式链条中有一个v[q][j] > j - q, 那么left[i][j]应该不大于q, 这样就导出了矛盾. 所以我们证明了, left[i][j]随j增加是不减的!!!
既然有单调性, 我们只需每次记录决策就可以了. 这样就可以过了.
在上述过程中我们提到了col[j][i], 类似的还有row[i][j], 问题转化为求一串数, 以每个数为中心的最长回文串长度. 这是个经典问题, 用Manacher算法就可以完美解决了.
代码实现:
1 #include <cstdio>
2 #include <cstring>
3
4 template<class T>
5 inline T min(T a, T b) { 6 return (a < b ? a : b); 7 } 8
9 const int kMaxN = 1000, kMaxM = 1000; 10 const int kMaxR = kMaxN * 2 - 1, kMaxC = kMaxM * 2 - 1; 11 const int kBufSize = 1999; 12 int n, m, v[kMaxR][kMaxC]; 13 int row[kMaxR][kMaxC], col[kMaxC][kMaxR]; 14 int tmp[kBufSize]; 15 int g[11][kBufSize], log2[kBufSize + 1]; 16 int f[kMaxR][kMaxC]; 17
18 void PreCalc(int *arr, int sz) { 19 for (int i = 1, k = 0; i < sz; ++ i) { 20 int p = k + arr[k]; 21 if (p > i && i + arr[2 * k - i] < p) arr[i] = arr[2 * k - i]; 22 else { 23 if (p > i) arr[i] = p - i; 24 while (i - arr[i] - 1 >= 0 && i + arr[i] + 1 < sz && tmp[i - arr[i] - 1] == tmp[i + arr[i] + 1]) ++ arr[i]; 25 k = i; 26 } 27 } 28 } 29
30 inline int AskMin(int l, int r) { 31 int st = log2[r - l + 1]; 32 return min(g[st][l], g[st][r - (1 << st) + 1]); 33 } 34
35 int main() { 36 for (int i = 2; i <= kBufSize; ++ i) { 37 log2[i] = log2[i - 1]; 38 if (!(i & (i - 1))) ++ log2[i]; 39 } 40 scanf("%d%d", &n, &m); 41 n = n * 2 - 1, m = m * 2 - 1; 42 for (int i = 0; i < n; i += 2) 43 for (int j = 0; j < m; j += 2) scanf("%d", &v[i][j]); 44 for (int i = 0; i < n; ++ i) { 45 for (int j = 0; j < m; ++ j) tmp[j] = v[i][j]; 46 PreCalc(row[i], m); 47 } 48 for (int j = 0; j < m; ++ j) { 49 for (int i = 0; i < n; ++ i) tmp[i] = v[i][j]; 50 PreCalc(col[j], n); 51 } 52 memset(f, 0x3f, sizeof(f)); 53 for (int i = 0; i < n; ++ i) { 54 for (int j = 0; j < m; ++ j) g[0][j] = col[j][i]; 55 for (int k = 1; (1 << k) <= m; ++ k) 56 for (int j = 0; j < m; ++ j) { 57 g[k][j] = g[k - 1][j]; 58 if (j + (1 << (k - 1)) < m) g[k][j] = min(g[k][j], g[k - 1][j + (1 << (k - 1))]); 59 } 60 for (int j = 0, las = 0; j < m; ++ j) { 61 while (AskMin(las, j) < j - las) ++ las; 62 f[i][j] = min(f[i][j], j - las); 63 } 64 for (int j = m - 1, las = m - 1; j >= 0; -- j) { 65 while (AskMin(j, las) < las - j) -- las; 66 f[i][j] = min(f[i][j], las - j); 67 } 68 } 69 for (int j = 0; j < m; ++ j) { 70 for (int i = 0; i < n; ++ i) g[0][i] = row[i][j]; 71 for (int k = 1; (1 << k) <= n; ++ k) 72 for (int i = 0; i < n; ++ i) { 73 g[k][i] = g[k - 1][i]; 74 if (i + (1 << (k - 1)) < n) g[k][i] = min(g[k][i], g[k - 1][i + (1 << (k - 1))]); 75 } 76 for (int i = 0, las = 0; i < n; ++ i) { 77 while (AskMin(las, i) < i - las) ++ las; 78 f[i][j] = min(f[i][j], i - las); 79 } 80 for (int i = n - 1, las = n - 1; i >= 0; -- i) { 81 while (AskMin(i, las) < las - i) -- las; 82 f[i][j] = min(f[i][j], las - i); 83 } 84 } 85 int ans = 0; 86 for (int i = 0; i < n; ++ i) 87 for (int j = 0; j < m; ++ j) 88 if (v[i][j]) 89 ans += (f[i][j] >> 1) + 1; 90 else if ((i & 1) && (j & 1)) 91 ans += (f[i][j] + 1) >> 1; 92 printf("%d\n", ans); 93 return 0; 94 }