【模板】B000_JM_子矩陣的和、差分(二維前綴和 / 一維差分)

一、子矩陣的和

輸入一個 n 行 m 列的整數矩陣,再輸入 q 個詢問,每個詢問包含四個整數 x1,y1,x2,y2,表示一個子矩陣的左上角座標和右下角座標。
對於每個詢問輸出子矩陣中所有數的和。

Input

第一行包含三個整數 n,m,q。
接下來 n 行,每行包含 m 個整數,表示整數矩陣。
接下來 q 行,每行包含四個整數 x1,y1,x2,y2,表示一組詢問。

Output

共 q 行,每行輸出一個詢問的結果。

3 4 3
1 7 2 4
3 6 2 8
2 1 2 3
1 1 2 2
2 1 3 4
1 3 3 4

17
27
21

數據範圍

1 ≤ n, m ≤ 1000,
1 ≤ q ≤ 200000,
1 ≤ x1 ≤ x2 ≤ n,
1 ≤ y1 ≤ y2 ≤ m,
−1000 ≤ 矩陣內元素的值 ≤ 1000


Q:二維前綴和怎麼求?
在這裏插入圖片描述
A:以上面的表格爲例,假如我要求 a[2][4]a[2][4] 的前綴和:

  • 先加上 a[1][4]a[1][4] 的前綴和,再加上 a[2][3]a[2][3] 的前綴和
  • 我們發現 a[1][3]a[1][3] 這個部分我們加了兩遍,所以我們需要再減去一遍 a[1][3]a[1][3]
  • 於是得出公式 a[i][j] += a[i][j1]+a[i1][j]a[i1][j1]a[i][j]\ +=\ a[i][j-1]+a[i-1][j]-a[i-1][j-1]

其實 a[1][4]a[1][4] 的前綴和 a[2][3]a[2][3] 的前綴和也就是一維前綴和

方法一:二維前綴和

Q:如何求子矩陣的和,比如深藍色區域的子矩陣的和?
在這裏插入圖片描述
我們每次要求的答案就是紅色圓圈所在的區域的值,對比上面這張圖我們能夠發現紅色區域的前綴和的值等於四個區域的前綴和的值減去(紅色區域+綠色區域),再減去(紅色區域+淺藍色區域),最後因爲紅色區域隱式地被減了兩次,我們需要再加一次回來。所以 ans 可得:

ans=a[x2][y2]a[x11][y2]a[x2][y11]+a[x11][y11]ans=a[x2][y2]-a[x1-1][y2]-a[x2][y1-1]+a[x1-1][y1-1]

import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
	static class Solution {
		void init() {
			Scanner sc = new Scanner(new BufferedInputStream(System.in));
			int r = sc.nextInt(), c = sc.nextInt(), q = sc.nextInt();
			long a[][] = new long[r+1][c+1];
			for (int i = 1; i <= r; i++)
			for (int j = 1; j <= c; j++) {
				a[i][j] = sc.nextInt();
			}
			for (int i = 1; i <= r; i++)
			for (int j = 1; j <= c; j++) {
				a[i][j] += (long) (a[i][j-1] + a[i-1][j] - a[i-1][j-1]);
			}

			while (q-- > 0) {
				int x1 = sc.nextInt(),y1 = sc.nextInt(),x2 = sc.nextInt(),y2 = sc.nextInt();
				long ans = (long) (a[x2][y2] - a[x1-1][y2] - a[x2][y1-1] + a[x1-1][y1-1]);
				System.out.println(ans);
			}
		}
	}
    public static void main(String[] args) throws IOException {  
        Solution s = new Solution();
		s.init();
    }
}

複雜度分析

  • 時間複雜度:O(R×C)O(R × C)
  • 空間複雜度:O(R×C)O(R × C)

二、差分模板題

你需要維護一個長度爲 N 的序列,支持 Q 次區間修改,每次將區間 [l,r][l,r] 內的數字加 val,最後輸出修改之後的序列。

Input

第一行兩個整數 N,Q.
第二行 N 個整數,代表序列初始值.
第三行開始連續 Q 行,每行三個整數 l,r,val.

Output

一行輸出 N 個數字,代表最後的序列.

Sample Input
5 2
1 2 3 4 5
1 3 1
4 5 1

Sample Output
2 3 4 5 6

Hint
0<=N,Q<=100000

方法一:差分

暴力做法是去枚舉區間,時間複雜度爲 O(NQ)O(NQ) 會超時;線段樹需要 O(QlogN)O(QlogN);差分只需要 O(Q)O(Q)

在這裏插入圖片描述

這裏的數組 d 就是數組 a 的差分數組,對差分數組求一遍前綴和運算,就可以得到原數組 a;可是這個數組 d 有什麼用?這裏先放一下,假設我對 a 數組的區間 [2,4][2, 4] 的數都加上 5,重新算一下 d 數組看會發生什麼?
在這裏插入圖片描述
對比一下,發現只有 d[2]d[2]d[5]d[5] 發生了改變(都減去了 5),那反過來,假如我要想給數組 aa 的區間 [l,r][l, r] 都加上 kk,那麼 d 數組變化的只有 d[l]d[l]d[r+1]d[r+1]

所以可以推出:如果要讓數組 a 的區間 [l,r][l,r] 的數都加上 k,我們只用讓 d[l]d[l] 加上 kk,然後讓 d[r+1]d[r+1] 減去 kk,然後對 d 數組的 [l,r][l, r] 求一遍前綴和即可求出修改後的 a 數組。

Q:這裏爲什麼要對 b[r+1] 減去 k 呢?

A:首先你要明確 d[i]=a[i]a[i1]d[i] = a[i] - a[i-1],所以 d[l]d[l] 加上了 kk,那麼數組 d 在區間 [l,n][l, n] 中的數都會加上 kk,而我們只需要將 [l,r][l,r] 的數加上 kk,所以 r+1r+1 之後的數被 “莫名其妙” 地加上了 kk,怎麼才能讓 [r+1,n][r+1, n] 的數都減去 k 呢,沒錯,d[k+1]=kd[k+1] -= k

import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
    static class Solution {
        void init() {
            Scanner sc = new Scanner(new BufferedInputStream(System.in));
            int n = sc.nextInt(), q = sc.nextInt(), a[] = new int[n+1], b[] = new int[n+2];

            for (int i = 1; i <= n; i++) {
                a[i] = sc.nextInt();
                b[i] = a[i] - a[i-1];
            }
            for (int i = 0; i < q; i++) {
                int l = sc.nextInt(), r = sc.nextInt(), k = sc.nextInt();
                b[l] += k;   b[r+1] -= k;
            }
            int[] s = Arrays.copyOf(b, b.length);
            for (int i = 1; i <= n; i++) {
                s[i] = s[i] + s[i-1];
                System.out.print(s[i] + " ");
            }
        }
    }
    public static void main(String[] args) throws IOException {  
        Solution s = new Solution();
        s.init();
    }
}

複雜度分析

  • 時間複雜度:O(max(N,Q))O(max(N, Q))
  • 空間複雜度:O(n)O(n)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章