前綴和與差分


前綴和
其實可以把它理解為數學上的數列的前n項和(對於一個一維數組的前綴和)。
我們定義對於一個數組a的前綴和數組s,s[i] = a[1]+a[2]+...+a[i].
 
二維前綴和
與一維前綴和類似,設s[i][j]表示所有a[i'][j']的和。(1≤i'≤i,1≤j'≤j)
有一點像“矩形的面積”那樣,把一整塊區域的值都加起來。
 
前綴和的用途
一般用來求區間和。
對於一維情況,現在我給出一個數列a,要求你回答m次詢問,每次詢問下標j到k的和。朴素的做法顯然是對於每次詢問都執行一次相加操作,然后輸出結果。這樣做是正確的,但是當m過大時就會導致計算次數過多而有可能超時。
超時的原因一目了然,重復計算。那么我們應該怎么改進這個方法呢?想象一下,我們如果先提前算好了每一個位置的前綴和,然后用s[k]-s[j],結果不就是我們這次詢問的答案嗎?這樣便會使計算量大大減小。
對於二維的區間和,也是類似的。
 
我們借助這個圖片研究一下。假設在這個矩陣(二維數組)中,我們要求和的是上圖中紅色區域。現在我們已經預處理出了所有點的前綴和,現在給定兩個點(x1,y1),(x2,y2),我們要求 以這兩個點連線為對角線的一個子矩陣的數值之和。暴力做法直接挨個加這個我就不再多說了,反正早晚都得TLE,我們重點考慮用前綴和的快速做法。
首先我們可以把s[x2][y2]求出來,它代表整個大矩形的前綴和,然后我們分別減去它左邊多出來的一塊的前綴和和下邊多出來一塊的前綴和,這樣就是最終答案了?
不是!這不是最終答案。可以發現,在我們剪掉這兩個多出的區域時,下邊的一小塊被減了兩次,但減兩次顯然是不合理的,我們應該加回來。。
所以對於一次的查詢答案ans應該等於 s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1]
這個二維前綴和也稱差分序列。
 
用差分實現區間操作
給定一個長度為n的數列a,要求支持操作add(L,R,k)表示對a[L]~a[R]的每個數都加上k。並求修改后的序列a。
暴力做法顯然,TLE,下一個。
我們考慮用差分的做法。這里 需要一個輔助數組c,c用來記錄某一個位置上的總改變量。c[i]表示的是i~n這些元素都加上c[i]這個數。我們對[L,R]區間進行加值操作,在c[L]處加一個k,在c[R+1]處就減去一個k。最后求序列的每個位置變成了多少,只需要求一下c數組的前綴和,然后和原數組按位相加就好。
這個結論的證明我不是太會。。呃記住它是對的就好啦。。
對於二維的情況,一個n*m的矩陣,要求支持操作add(x1,y1,x2,y2,a),表示對於以(x1,y1)為左下角,(x2,y2)為右上角的矩形區域,每個元素都加上a。要求修改后的矩陣。
我們的做法和一維類似。用數組c存儲總改變量。在c[x1][y1]處加上a,在c[x2+1][y1]和c[x1][y2+1]處減a,在c[x2+1][y2+1]再加上a。最后(i,k)位置上的數值就是c數組在(i,k)位置的前綴和。
 
差分
用差分實現區間操作

取C數組為修改數組,C[i]表示的是i~n這些元素都加上C[i]這個數
樣例:
ADD(1,3,1)
ADD(2,4,4)
ADD(3,7,3)
我們對[L,R]區間進行加value操作,在C[L]處加上value,在C[R+1]處減去value
最后求序列的每個位置變成了多少,只要看一下這個位置上C的前綴和就可以

升級版:對於一個n*m的表格,要求支持操作ADD(x1,y1,x2,y2,a),表示對於以(x1,y1)為左下角,(x2,y2)為右上角的矩形區域,每個元素都加上a。問最后的表格的樣子
和一維版的做法類似。
用數組C存修改信息。在C[x1][y1]處加上a,在C[x2+1][y1]和C[x1][y2+1]處減a,在C[x2+1][y2+1]再加上a。
最后(i,k)位置上的數值就是C數組在(i,k)位置的前綴和。
樹狀數組區間修改單點查詢的時候要用!

 

上述知識點為我在網上dalao博主找的較好的解釋!

 

我自己對用差分對區間操作的理解,我們對[L,R]區間進行加value操作,在C[L]處加上value,在C[R+1]處減去value,因為最后要用c數組的前綴和與原始數組分別相加,所以在C[L]處加上value只對L以后數有影響,在L以后的前綴和都加上了value,因為要求的作用范圍是[L,R],所以R以后的前綴和不能產生影響,則應該在C[R+1]處減去value,以與前面加的value相抵消。二維數組同理。

下面是我遇到的例題:

上交大OJ 1002:二哥種花生

Description

二哥在自己的后花園里種了一些花生,也快到了收獲的時候了。這片花生地是一個長度為L、寬度為W的矩形,每個單位面積上花生產量都是獨立的。他想知道,對於某個指定的區域大小,在這么大的矩形區域內,花生的產量最大會是多少。

Input Format

第1行有2個整數,長度L和寬度W。

第2行至第L+1行,每行有W個整數,分別表示對應的單位面積上的花生產量A( 0≤A<10 )。

第L+2行有2個整數,分別是指定的區域大小的長度a和寬度b。

Output Format

輸出一個整數m,表示在指定大小的區域內,花生最大產量為m。

Sample Input

4 5
1 2 3 4 5
6 7 8 0 0
0 9 2 2 3
3 0 0 0 1
3 3

Sample Output

38

樣例解釋

左上角:38 = (1+2+3) + (6+7+8) + (0+9+2)

數據范圍

對於30%的數據: 1≤L,W≤100;

對於100%的數據: 1≤L,W≤1000。

全部區域大小滿足:1≤a≤L,1≤b≤W 。

 

分析:這里就用到前綴和來求解遍歷,以防超時!

AC code:

 1 #include <iostream>
 2 using namespace std;
 3 const int maxn = 1000 + 5;
 4 int c[maxn][maxn] = {0};
 5 int main() {
 6     int L, W;
 7     cin >> L >> W;
 8     for(int i = 1; i <= L; i++)
 9         for(int j = 1; j <= W; j++) {
10             cin >> c[i][j];
11             c[i][j] = c[i][j] + c[i][j-1] + c[i-1][j] - c[i-1][j-1];      //求前綴和
12         }
13 
14     int a, b;
15     long max=0, sum;
16     cin >> a >> b;
17     
18     for( int i = 1; i <= L - a + 1; i++) {
19         for(int j = 1; j <= W - b + 1; j++) {
20             sum = c[i+a-1][j+b-1] - c[i-1][j+b-1] - c[i+a-1][j-1] + c[i-1][j-1];
21             if(sum > max)
22                 max = sum;
23         }
24     }
25     cout << max;
26     return 0;
27 }

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM