藍橋杯專題四 算法競賽進階指南0x03:前綴和與差分

一維前綴和:

適用題型:對於長度爲 n 的序列,進行 m 次詢問,每次查詢區間 [L, R] 之間的數的和,樸素做法的複雜度爲 n * m,而使用前綴和就將複雜度將爲了 m 。

已知a[1], a[2], ..., a[i] 
則前綴和數組 s[1] = a[1], s[2] = s[1] + a[2], s[i] = s[i-1] + a[i] (規定s[0] = 0)

那麼,對於每次詢問,s[r] - s[l-1] 就是我們要求的區間和了。

二維前綴和:

類似與一維前綴和,存在公式

s[i][j] = s[i-1][j] + s[i][j-1] + a[i][j] - s[i-1][j-1];

當要求某一矩形區域的和時,只需要知道在對角線上的兩個頂點的 x, y 座標

假設:左上(x1, y1),右下(x2, y2)

那麼區域和計算方法爲:

ans = s[x2][y2] - s[x2][y1-1] - s[x1-1][y2] + s[x1-1][y1-1]

模板題:子矩陣的和 題目來源:y總的AcWing

題目描述:

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

輸入格式

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

輸出格式

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

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
typedef long long LL;
using namespace std;
const int MAXN = 1010;
int n, m, q, x1, y1, x2, y2;
LL a[MAXN][MAXN];
LL s[MAXN][MAXN];
int main() {
	scanf("%d %d %d", &n, &m, &q);
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= m; j++) {
			scanf("%lld", &a[i][j]);
			s[i][j] = s[i-1][j] + s[i][j-1] + a[i][j] - s[i-1][j-1];
		}
	}
	while(q--) {
		scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
		printf("%lld\n", s[x2][y2] - s[x2][y1-1] - s[x1-1][y2] + s[x1-1][y1-1]);
	}

	return 0;
}

差分:

對於一個給定的數列 A,它的差分數列 B 定義爲:
B[1] = A[1], B[i] = A[i] - A[i-1] (2 <= i <= n)
容易發現,“前綴和” 與 “差分” 是一對互逆運算,差分序列 B 的前綴和就是原序列 A,前綴和序列 S 的差分序列也是原序列 A。

給你一串長度爲n的數列a1,a2,a3......an,要求對a[L]~a[R]進行m次操作:

操作一:將a[L]~a[R]內的元素都加上c

操作二:將a[L]~a[R]內的元素都減去c

最後再給出一個詢問求a[L]-a[R]內的元素之和?

你會怎麼做呢?你可能會想,我對於m次操作每次都遍歷一遍a[L]~a[R],
給區間裏的數都加上c或減去c,最後再求一次前綴和就行了,但這種樸素做法複雜度還是 m * n
對於1<=n,m<=1e5這個數據範圍來說直接就GG了
是這個時候我們的差分就該派上用場了,我們新開一個數組b,儲存每一次的修改操作
最後求前綴和的時候統計一下就能快速的得到正確答案了,

要對 l 到 r 內的數加 C 時:

b[l] += C, b[r+1] -= C ,就完成了操作

利用差分的概念,就沒必要對區間內所有的數進行操作了。

例題: 差分 (模板題 hulu面試題)

題目描述:

輸入一個長度爲n的整數序列。 接下來輸入m個操作,每個操作包含三個整數l, r, c,表示將序列中[l, r]之間的每個數加上c。
請你輸出進行完所有操作後的序列。

輸入格式

第一行包含兩個整數n和m。 第二行包含n個整數,表示整數序列。 接下來m行,每行包含三個整數l,r,c,表示一個操作。

輸出格式

共一行,包含n個整數,表示最終序列。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n, m, l, r, c;
int a[100010], b[100010];
// 其實只需要一個數組就可以 
int main() {
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
	}
	for(int i = 1; i <= n; i++) {
		b[i] = a[i] - a[i-1];
	}
	while(m--) {
		scanf("%d %d %d", &l, &r, &c);
		b[l] += c, b[r+1] -= c;
	}
	for(int i = 1; i <= n; i++) {
		printf("%d ", b[i] += b[i-1]);
		// 注意這裏求新數組的方法 
	}
	
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章