[R39C]网格求和


对于 40%40\% 的数据,1n,m1001 \le n, m \le 100

对于 100%100\% 的数据,1n,m20001 \le n, m \le 20000Ai,j1090 \le A_{i,j} \le 10^9

思路

对于每个 Bi,jB_{i,j},需要把第 ii 行的所有格子和第 jj 列的所有格子求和。第 ii 行和第 jj 列的并集里,Ai,jA_{i,j} 同时属于行和列,被加了两次,需要减去一次,即:

Bi,j=kAi,k+kAk,jAi,jB_{i,j} = \sum_{k} A_{i,k} + \sum_{k} A_{k,j} - A_{i,j}

如果对每个 (i,j)(i,j) 都暴力求和,复杂度是 O(nm(n+m))O(nm(n+m)),无法承受。

预处理两个一维前缀和数组即可:

  • rowSum[i]=kAi,k\mathrm{rowSum}[i] = \sum_k A_{i,k}:第 ii 行的元素和;
  • colSum[j]=kAk,j\mathrm{colSum}[j] = \sum_k A_{k,j}:第 jj 列的元素和。

读入时顺便累加:每读入一个 Ai,jA_{i,j},累加到 rowSum[i]\mathrm{rowSum}[i]colSum[j]\mathrm{colSum}[j]。随后对每个 (i,j)(i,j) 输出 rowSum[i]+colSum[j]Ai,j\mathrm{rowSum}[i] + \mathrm{colSum}[j] - A_{i,j}

注意值域:单个行列和最大为 2000×109=2×10122000 \times 10^9 = 2 \times 10^{12}BB 最大约 4×10124 \times 10^{12},超出 32 位整数范围,需要用 64 位整数。

复杂度

  • 时间:读入 O(nm)O(nm),输出 O(nm)O(nm),总计 O(nm)O(nm)
  • 空间:O(nm)O(nm) 存储网格 AA,另需 O(n+m)O(n+m) 存行列和。

仓颉实现

import std.env.*
import std.convert.*

main(): Int64 {
    let reader = getStdIn()
    let first = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
    let n = first[0]
    let m = first[1]
    let nn = n
    let mm = m
    var a = Array<Array<Int64>>(nn, { _ => Array<Int64>(mm, { _ => 0 }) })
    let rowSum = Array<Int64>(nn, { _ => 0 })
    let colSum = Array<Int64>(mm, { _ => 0 })
    for (i in 0..nn) {
        let line = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
        var rs = Int64(0)
        for (j in 0..mm) {
            a[i][j] = line[j]
            rs += line[j]
            colSum[j] += line[j]
        }
        rowSum[i] = rs
    }
    let sb = StringBuilder()
    for (i in 0..nn) {
        for (j in 0..mm) {
            sb.append(rowSum[i] + colSum[j] - a[i][j])
            if (j + 1 < mm) {
                sb.append(" ")
            }
        }
        sb.append("\n")
    }
    print(sb.toString())
    return 0
}