[除草]BZOJ 1414 [ZJOI2009]对称的正方形

题目大意:

给一个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算法就可以完美解决了.

 

代码实现:

View Code
 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 }

你可能感兴趣的:(ZOJ)