zoukankan      html  css  js  c++  java
  • 2020牛客多校第九场J-The Escape Plan of Groundhog

    https://ac.nowcoder.com/acm/contest/5674/J

    题意

    给出一个只包含0、1的矩阵,求其中满足如下条件的子矩阵数

    1. 边界全部为1
    2. 内部1和0的数量差绝对值不超过1

    题解

    如果需要做到 (O(n^3 )),套路一般都是:枚举上下行边界,对于列扫一遍,用前缀和等维护。

    那么这里四条边上都要为1,那么枚举上下边界后,肯定要找一段在这两行都是1的连续的列区间。然后在这个区间里找。

    枚举每一列,如果这一列也都是1,就可以统计进去。用一个前缀和维护,在原矩阵中为0则当做-1,否则当做1。那么每次找到合法的列,查询前面的前缀和是否有和它相差1以内的,计入答案。然后把自己的前缀和加入统计。

    (O(n^2 m)) 或$ O(nm^2 )$

    队友一眼秒了,tql

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    struct READ {
        inline char read() {
        #ifdef _WIN32
            return getchar();
        #endif
            static const int IN_LEN = 1 << 18 | 1;
            static char buf[IN_LEN], *s, *t;
            return (s == t) && (t = (s = buf) + fread(buf, 1, IN_LEN, stdin)), s == t ? -1 : *s++;
        }
        template <typename _Tp> inline READ & operator >> (_Tp&x) {
            static char c11, boo;
            for(c11 = read(),boo = 0; !isdigit(c11); c11 = read()) {
                if(c11 == -1) return *this;
                boo |= c11 == '-';
            }
            for(x = 0; isdigit(c11); c11 = read()) x = x * 10 + (c11 ^ '0');
            boo && (x = -x);
            return *this;
        }
    } in;
    
    const int N = 505;
    int a[N][N];
    int sum[N][N], check[N][N];
    int t[N*N*3];
    int s[N];
    ll ans = 0;
    void calc(int a, int b, int x, int y) {
        s[x-1] = N * N;
        vector<int> tmp;
        for (int i = x; i <= y; i++) {
            s[i] = s[i-1] + sum[b-1][i] - sum[a][i];
            if (check[b][i] - check[a-1][i] == b - a + 1) {
                ans += t[s[i-1]] + t[s[i-1]+1] + t[s[i-1]-1];
                t[s[i]]++;
                tmp.push_back(s[i]);
            }
        }
        for (int e : tmp) t[e] = 0;
    }
    int main() {
        int n, m; in >> n >> m;
        for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) in >> a[i][j];
        for (int j = 1; j <= m; j++) {
            for (int i = 1; i <= n; i++) {
                sum[i][j] = sum[i-1][j] + (a[i][j] == 0 ? -1 : 1);//要加括号
                check[i][j] = check[i-1][j] + a[i][j]; 
            }
        }
        for (int i = 1; i <= n; i++) {
            for (int j = i + 1; j <= n; j++) {
                int l = 1;
                for (int k = 1; k <= m + 1; k++) {
                    if (a[i][k] == 0 || a[j][k] == 0) {
                        if (k - l >= 2) calc(i, j, l, k - 1);
                        l = k + 1;
                    }
                }
            }
        }
        printf("%lld
    ", ans);
        return 0;
    }
    
  • 相关阅读:
    P5362 [SDOI2019]连续子序列 思维题
    P5360 [SDOI2019]世界地图 虚树+最小生成树
    P4565 [CTSC2018]暴力写挂 边分治+虚树
    BZOJ2870. 最长道路tree 并查集/边分治
    P4103 [HEOI2014]大工程 虚树
    P4220 [WC2018]通道 虚树+边分治
    P3261 [JLOI2015]城池攻占 可并堆
    积水问题
    23. 合并K个排序链表
    21. 合并两个有序链表
  • 原文地址:https://www.cnblogs.com/artoriax/p/13633288.html
Copyright © 2011-2022 走看看