BZOJ 1414. [ZJOI2009]对称的正方形
先给每个两个点之间加一个空,这样直接枚举每个点当中心,就不用讨论正方形的长度是奇数还是偶数了。
枚举每个点当中心,二分正方形多长,$O(1)$ check 用二维哈希做即可。
#include <bits/stdc++.h> namespace IO { char buf[1 << 21], buf2[1 << 21], a[20], *p1 = buf, *p2 = buf, hh = '\n'; int p, p3 = -1; void read() {} void print() {} inline int getc() { return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++; } inline void flush() { fwrite(buf2, 1, p3 + 1, stdout), p3 = -1; } template <typename T, typename... T2> inline void read(T &x, T2 &... oth) { T f = 1; x = 0; char ch = getc(); while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getc(); } while (isdigit(ch)) { x = x * 10 + ch - 48; ch = getc(); } x *= f; read(oth...); } template <typename T, typename... T2> inline void print(T x, T2... oth) { if (p3 > 1 << 20) flush(); if (x < 0) buf2[++p3] = 45, x = -x; do { a[++p] = x % 10 + 48; } while (x /= 10); do { buf2[++p3] = a[p]; } while (--p); buf2[++p3] = hh; print(oth...); } } const int N = 2100; unsigned a[N][N], ha[3][N][N], base[2][N]; const unsigned seed[2] = {2333, 233}; int n, m; unsigned gethash(int t, int x1, int y1, int x2, int y2) { return ha[t][x2][y2] - ha[t][x1 - 1][y2] * base[1][x2 - x1 + 1] - ha[t][x2][y1 - 1] * base[0][y2 - y1 + 1] + ha[t][x1 - 1][y1 - 1] * base[0][y2 - y1 + 1] * base[1][x2 - x1 + 1]; } bool check(int mid, int i, int j) { int x1 = i - mid + 1, x2 = i + mid - 1, y1 = j - mid + 1, y2 = j + mid - 1; //assert(x1 >= 1 && x2 <= n && y1 >= 1 && y2 <= m); unsigned k1 = gethash(0, x1, y1, x2, y2), k2 = gethash(1, n - x2 + 1, y1, n - x1 + 1, y2), k3 = gethash(2, x1, m - y2 + 1, x2, m - y1 + 1); if (k1 != k2 || k1 != k3) return 0; return 1; } int main() { IO::read(n, m); for (int i = base[0][0] = base[1][0] = 1; i < N; i++) for (int j = 0; j < 2; j++) base[j][i] = base[j][i - 1] * seed[j]; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) IO::read(a[2 * i - 1][2 * j - 1]); n = n * 2 - 1; m = m * 2 - 1; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { ha[0][i][j] = ha[0][i][j - 1] * seed[0] + a[i][j]; ha[1][i][j] = ha[1][i][j - 1] * seed[0] + a[n - i + 1][j]; ha[2][i][j] = ha[2][i][j - 1] * seed[0] + a[i][m - j + 1]; } for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { ha[0][i][j] += ha[0][i - 1][j] * seed[1]; ha[1][i][j] += ha[1][i - 1][j] * seed[1]; ha[2][i][j] += ha[2][i - 1][j] * seed[1]; } int ans = 0; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { if (i + j & 1) continue; int l = 1, r = std::min(std::min(i, n - i + 1), std::min(j, m - j + 1)), res = 0; while (l <= r) { int mid = l + r >> 1; if (check(mid, i, j)) l = mid + 1, res = mid; else r = mid - 1; } //assert(check(1, i, j)); if (i & 1) res++; ans += res / 2; } printf("%d\n", ans); return 0; }
在黑暗爆炸oj上过了,在lydsy上T了。
加了个剪枝过了。二分的mid可以先设为5,因为对称正方形很难构造?不会有太多长度5以上的,这样就能少二分几次。
#include <bits/stdc++.h> namespace IO { char buf[1 << 21], a[20], *p1 = buf, *p2 = buf; void read() {} inline int getc() { return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++; } template <typename T, typename... T2> inline void read(T &x, T2 &... oth) { T f = 1; x = 0; char ch = getc(); while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getc(); } while (isdigit(ch)) { x = x * 10 + ch - 48; ch = getc(); } x *= f; read(oth...); } } const int N = 2100; unsigned a[N][N], ha[3][N][N], base[2][N]; const unsigned seed[2] = {1015415441, 1115415451}; int n, m; unsigned gethash(int t, int x1, int y1, int x2, int y2) { return ha[t][x2][y2] - ha[t][x1 - 1][y2] * base[1][x2 - x1 + 1] - ha[t][x2][y1 - 1] * base[0][y2 - y1 + 1] + ha[t][x1 - 1][y1 - 1] * base[0][y2 - y1 + 1] * base[1][x2 - x1 + 1]; } bool check(int mid, int i, int j) { int x1 = i - mid + 1, x2 = i + mid - 1, y1 = j - mid + 1, y2 = j + mid - 1; //assert(x1 >= 1 && x2 <= n && y1 >= 1 && y2 <= m); unsigned k1 = gethash(0, x1, y1, x2, y2), k2 = gethash(1, n - x2 + 1, y1, n - x1 + 1, y2), k3 = gethash(2, x1, m - y2 + 1, x2, m - y1 + 1); if (k1 != k2 || k1 != k3) return 0; return 1; } int main() { IO::read(n, m); for (int i = base[0][0] = base[1][0] = 1; i < N; i++) for (int j = 0; j < 2; j++) base[j][i] = base[j][i - 1] * seed[j]; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) IO::read(a[2 * i - 1][2 * j - 1]); n = n * 2 - 1; m = m * 2 - 1; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { ha[0][i][j] = ha[0][i][j - 1] * seed[0] + a[i][j]; ha[1][i][j] = ha[1][i][j - 1] * seed[0] + a[n - i + 1][j]; ha[2][i][j] = ha[2][i][j - 1] * seed[0] + a[i][m - j + 1]; } for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { ha[0][i][j] += ha[0][i - 1][j] * seed[1]; ha[1][i][j] += ha[1][i - 1][j] * seed[1]; ha[2][i][j] += ha[2][i - 1][j] * seed[1]; } int ans = 0; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { if ((i + j) & 1) continue; int l = 1, r = std::min(std::min(i, n - i + 1), std::min(j, m - j + 1)), res = 0; int mid = std::min(5, r); while (l <= r) { if (check(mid, i, j)) l = mid + 1, res = mid; else r = mid - 1; mid = l + r >> 1; } //assert(check(1, i, j)); if (i & 1) res++; ans += res / 2; } printf("%d\n", ans); return 0; }