[除草]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 }
posted @ 2012-05-27 21:56  zcwwzdjn  阅读(1689)  评论(0编辑  收藏  举报