题解:洛谷 AT_abc461_d Count Subgrid Sum = K

【题目来源】

洛谷:AT_abc461_d [ABC461D] Count Subgrid Sum = K - 洛谷

【题目描述】

There is an \(H \times W\) grid, and each cell contains an integer \(0\) or \(1\).
The information of the integer written in each cell is given as \(H\) strings \(S_1, S_2, \dots, S_H\) of length \(W\). If the \(j\)-th character of \(S_i\) is 0, then \(0\) is written in the cell at the \(i\)-th row from the top and the \(j\)-th column from the left of the grid; if the \(j\)-th character of \(S_i\) is 1, then \(1\) is written in that cell.

Find the number of rectangular regions where the sum of the written integers equals \(K\).
More formally, find the number of quadruples of integers \((r_1, c_1, r_2, c_2)\) satisfying all of the following conditions:

  • \(1 \le r_1 \le r_2 \le H\)
  • \(1 \le c_1 \le c_2 \le W\)
  • The sum of the integers written in the cell at the \(i\)-th row from the top and the \(j\)-th column from the left, over all pairs of integers \((i, j)\) satisfying \(r_1 \le i \le r_2, c_1 \le j \le c_2\), equals \(K\).

有一个 \(H \times W\) 的网格,每个单元格中写有一个整数 \(0\)\(1\)
每个单元格中写入的整数的信息由 \(H\) 个长度为 \(W\) 的字符串 \(S_1, S_2, \dots, S_H\) 给出。如果 \(S_i\) 的第 \(j\) 个字符是 0,则网格中从上往下第 \(i\) 行、从左往右第 \(j\) 列的单元格中写有 \(0\);如果 \(S_i\) 的第 \(j\) 个字符是 1,则写有 \(1\)

求其中所写整数之和等于 \(K\) 的矩形区域的数量。
更形式化地说,求满足以下所有条件的整数四元组 \((r_1, c_1, r_2, c_2)\) 的数量:

  • \(1 \le r_1 \le r_2 \le H\)
  • \(1 \le c_1 \le c_2 \le W\)
  • 对于所有满足 \(r_1 \le i \le r_2, c_1 \le j \le c_2\) 的整数对 \((i, j)\),网格中从上往下第 \(i\) 行、从左往右第 \(j\) 列的单元格中所写整数之和等于 \(K\)

【输入】

The input is given from Standard Input in the following format:

\(H\) \(W\) \(K\)
\(S_1\)
\(S_2\)
\(\vdots\)
\(S_H\)

【输出】

Output the answer.

【输入样例】

3 4 3
1001
1101
0110

【输出样例】

8

【核心思想】

  1. 问题分析:给定一个 \(H \times W\)\(01\) 网格,需要找出有多少个子矩形区域的元素之和恰好等于 \(K\)。这是一个二维前缀和 + 降维 + 哈希表问题。直接枚举四个边界的时间复杂度为 \(O(H^2 W^2)\),需要优化。

  2. 算法选择

    • 列前缀和:预处理每列在行方向上的前缀和,快速计算任意行区间的列和
    • 降维策略:枚举上下边界 \([r_1, r_2]\),将二维问题转化为一维问题
    • 哈希表(HashMap):对于固定行区间,用哈希表存储横向前缀和频率,快速统计和为 \(K\) 的子数组个数
  3. 关键步骤

    • 列前缀和预处理
      • 计算 \(colSum[j][i] = \sum_{t=1}^{i} a[t][j]\),表示第 \(j\) 列前 \(i\) 行的和
    • 枚举上下边界\(r_1\)\(1\)\(H\)\(r_2\)\(r_1\)\(H\)):
      • 对于当前行区间 \([r_1, r_2]\),计算每列在该区间的和:\(colVal_j = colSum[j][r_2] - colSum[j][r_1-1]\)
      • 问题转化为:在数组 \([colVal_1, colVal_2, \ldots, colVal_W]\) 中,找和为 \(K\) 的子数组个数
    • 一维子数组和问题(哈希表优化):
      • 初始化 mp[0] = 1,表示前缀和为 \(0\) 出现 \(1\)
      • 遍历列 \(j\)\(1\)\(W\)
        • 累加当前列值:\(sum = sum + colVal_j\)
        • 查询哈希表中 \(sum - K\) 的出现次数,累加到答案 \(ans\)
        • 更新哈希表:mp[sum]++
    • 输出答案 \(ans\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(H^2 \cdot W)\),枚举上下边界 \(O(H^2)\),每轮横向遍历 \(O(W)\)
    • 空间复杂度:\(O(H \cdot W)\),存储列前缀和数组和哈希表
  5. 降维与哈希表的核心思想

    • 降维策略:通过枚举上下边界,将二维子矩形和问题转化为多个一维子数组和问题
    • 列前缀和:快速计算固定行区间内每列的和,避免重复计算
    • 哈希表优化:将一维子数组和问题从 \(O(W^2)\) 优化到 \(O(W)\),利用前缀和之差等于 \(K\) 的性质
    • 组合计数:总方案数 = 所有行区间对应的横向子数组方案数之和
    • 适用于二维子矩阵统计、降维优化、子数组计数等场景

【算法标签】

普及 #前缀和

【代码详解】

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 505;  // 定义最大网格尺寸
int h, w, k, ans;  // 行数h,列数w,目标和k,答案ans
int a[N][N];  // 网格数值数组
int colSum[N][N];  // 列前缀和数组

signed main()  // 主函数
{
    cin >> h >> w >> k;  // 输入行数、列数、目标和
    for (int i=1; i<=h; i++)  // 读取网格数据
        for (int j=1; j<=w; j++)
            scanf("%1d", &a[i][j]);  // 以一位整数形式读取字符
    for (int j=1; j<=w; j++)  // 计算列前缀和
        for (int i=1; i<=h; i++)
            colSum[j][i] = colSum[j][i-1] + a[i][j];  // 计算列j的前i行和
    for (int r1=1; r1<=h; r1++)  // 枚举矩形上边界
        for (int r2=r1; r2<=h; r2++)  // 枚举矩形下边界
        {
            map<int, int> mp;  // 前缀和频次哈希表
            mp[0] = 1;  // 初始化前缀和为0的情况
            int sum = 0;  // 当前前缀和
            for (int j=1; j<=w; j++)  // 从左到右遍历列
            {
                int colVal = colSum[j][r2] - colSum[j][r1-1];  // 当前列在行区间[r1,r2]的和
                sum += colVal;  // 累加到总前缀和
                auto it = mp.find(sum - k);  // 寻找sum-k的前缀和
                if (it != mp.end())  // 如果找到
                {
                    ans += it->second;  // 累加方案数
                }
                mp[sum]++;  // 记录当前前缀和
            }
        }
    cout << ans << endl;  // 输出答案
    return 0;  // 程序正常结束
}

【运行结果】

3 4 3
1001
1101
0110
8
posted @ 2026-06-14 10:48  团爸讲算法  阅读(14)  评论(0)    收藏  举报