• [ZJOI2009]对称的正方形 (二维哈希+二分)


    题意

    求矩阵中上下对称且左右对称的正方形子矩阵的个数。

    思路

    快速对比矩阵是否上下对称或者左右对称可以考虑对二维矩阵哈希。

    哈希之后通过枚举正方形中心点位置,对正方形边长进行二分。

    在枚举正方形中心点时,需考虑正方形边长分别为奇偶的情况,即中心点为格子交接点还是格子中心点。

    二维矩阵哈希值维护:

    (hash[x][y] = hash[x][y-1] imes base1 + hash[x - 1][y] imes base2 + value[x][y])

    二维矩阵哈希值求解:

    (ans = hash[x][y] - hash[x - len_x][y] imes fac1[len_x] - hash[x][y -len_y] imes fac2[len_y] + hash[x - len_x][y - len_y] imes fac1[len_x] imes fac2[len_y])

    其中 (fac1), (fac2) 分别为 (base1), (base2)(len) 次方

    代码

    #include <cstdio>
    #include <algorithm>
    #include <iostream>
    
    using namespace std;
    
    typedef unsigned long long ull;
    typedef long long ll;
    const int maxn = 2010;
    const int base1 = 87;
    const int base2 = 31;
    
    int n, m;
    int mp[maxn][maxn];
    int lr[maxn][maxn];
    int up[maxn][maxn];
    
    ull hash_mp[maxn][maxn];
    ull hash_lr[maxn][maxn];
    ull hash_up[maxn][maxn];
    ull fac1[maxn], fac2[maxn];
    
    bool check(int x, int y, int len) {
        if (x > n || y > m) return 0;
        if (x < len || y < len) return 0;
        ull ans1 = hash_mp[x][y] - hash_mp[x - len][y] * fac2[len] - hash_mp[x][y - len] * fac1[len]
                + hash_mp[x - len][y - len] * fac1[len] * fac2[len];
        int cow_y = m - (y - len);
        ull ans2 = hash_lr[x][cow_y] - hash_lr[x - len][cow_y] * fac2[len] - hash_lr[x][cow_y - len] * fac1[len]
                + hash_lr[x - len][cow_y - len] * fac1[len] * fac2[len];
        if (ans1 != ans2) return 0;
        int row_x = n - (x - len);
        ull ans3 = hash_up[row_x][y] - hash_up[row_x - len][y] * fac2[len] - hash_up[row_x][y - len] * fac1[len]
                + hash_up[row_x - len][y - len] * fac1[len] * fac2[len];
        if (ans1 != ans3) return 0;
        return (ans1 == ans3 && ans2 == ans3);
    }
    
    void solve() {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= m; ++j)
                scanf("%d", &mp[i][j]);
        }
    
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= m; ++j) {
                lr[i][j] = mp[i][m - j + 1];
                up[i][j] = mp[n - i + 1][j];
            }
        }
    
        fac1[0] = fac2[0] = 1;
        for (int i = 1; i <= n; ++i) fac1[i] = fac1[i - 1] * base1;
        for (int i = 1; i <= m; ++i) fac2[i] = fac2[i - 1] * base2;
    
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= m; ++j) {
               hash_mp[i][j] = hash_mp[i][j - 1] * base1 + mp[i][j];
               hash_lr[i][j] = hash_lr[i][j - 1] * base1 + lr[i][j];
               hash_up[i][j] = hash_up[i][j - 1] * base1 + up[i][j];
            }
        }
    
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= m; ++j) {
                hash_mp[i][j] += hash_mp[i - 1][j] * base2;
                hash_lr[i][j] += hash_lr[i - 1][j] * base2;
                hash_up[i][j] += hash_up[i - 1][j] * base2;
            }
        }
    
        ll ans = 0;
        int R = max(n, m);
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= m; ++j) {
                int l = 1, r = R;
                while (l <= r) {
                    int mid = l + r >> 1;
                    if (check(i + mid, j + mid, mid << 1 | 1)) {
                        l = mid + 1;
                    } else {
                        r = mid - 1;
                    }
                }
    //            cout << "~ " << l << " " << r << endl;
                ans += r;
            }
        }
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= m; ++j) {
                int l = 1, r = R;
                while (l <= r) {
                    int mid = l + r >> 1;
                    if (check(i + mid, j + mid, mid << 1)) {
                        l = mid + 1;
                    } else {
                        r = mid - 1;
                    }
                }
    //            cout << "~~ " << i << " " << j << " " << l << " " << r << endl;
                ans += r;
            }
        }
        printf("%lld
    ", ans + n * m);
    }
    
    int main() {
        int T = 1;
    //    scanf("%d", &T);
        while (T--)
            solve();
        return 0;
    }
    
  • 相关阅读:
    fdisk 分区
    fdisk 添加逻辑分区
    centos7 bond0 双网卡配置
    查看centos7启动项
    本地yum源安装docker
    cobbler Ubuntu16.04 安装
    docker-ce-17.03.2 离线安装RPM包
    day14 生成器的进阶
    day13迭代器与生成器
    day12闭包,装饰器
  • 原文地址:https://www.cnblogs.com/acerkoo/p/13808162.html
Copyright © 2020-2023  润新知