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

 在黑暗爆炸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;
}
View Code

 

posted @ 2020-01-28 16:26  Mrzdtz220  阅读(171)  评论(0编辑  收藏  举报