前綴和以及差分問題:
導論:
該博客記錄前綴和問題以及差分的解題步驟與相應公式;
理解其中變化,有不完善的地方慢慢補全;
如果有錯誤歡迎指出!
前綴和:
首先需要知道前綴和的概念:即數組該位置之前的元素之和。
還有一個重要的點,在進行前綴和的運算時,下標從1開始,設數組a[0]=0;
比如a[5] = {0,1,2,3,4};
求a[1]的前綴和:a[1];
求a[2]的前綴和:a[1]+a[2];
......
為什么下標要從1 開始:為了方便后面的計算,避免下標轉換,設為零,不影響結果
前綴和的作用: 快速求出元素組中某段區間的和
一維數組的前綴和問題:
求數組a中(l,r)區間的和 --->用到前綴和
-
需要定義兩個數組,第一個為原始數組(a[]),第二個為前綴和數組(s[])
//初始化原數組 int[] arr = new int[x]; for (int i = 1; i <= n; i++) { arr[i] = sc.nextInt(); } -
公式:s[i] = s[i-1]+a[i] {其中s[i]表示a數組的前i項的和}
//前綴和的計算 int[] s = new int[x]; for (int i = 1; i <=n ; i++) { s[i] = s[i-1]+arr[i]; } -
輸入區間范圍(l,r),s[r]-s[l-1]的結果就是所求區間的和
sum[r] =a[1]+a[2]+a[3]+a[l-1]+a[l]+a[l+1]......a[r]; sum[l-1]=a[1]+a[2]+a[3]+a[l-1]; sum[r]-sum[l-1]=a[l]+a[l+1]+......+a[r];while (m-- !=0){ int l = sc.nextInt(); int r = sc.nextInt(); System.out.println(s[r]-s[l-1]); }
二維數組的前綴和問題:
方法與一維數組大體相同:需要中間數組s[i][j]
-
定義兩個二維數組,第一個為原始數組
a[][],第二個為臨時數組b[][]// 初始化原始數組 int[][] arr = new int[n+1][m+1]; for (int i = 1; i <= n; i++) { for (int j = 1; j <=m ; j++) { arr[i][j] = sc.nextInt(); } } -
公式:
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + arr[i][j]
//定義s二維數組,求解前綴和s數組 int[][] s = new int[n+1][m+1]; for (int i = 1; i <= n; i++) { for (int j = 1; j <=m ; j++) { s[i][j] = s[i-1][j]+s[i][j-1]-s[i-1][j-1]+arr[i][j]; } } -
輸入區間范圍(x1,y1,x2,y2),
s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]的結果就是所求區間的和;
//求解前綴和 while(q-- !=0){ int x1 = sc.nextInt(); int y1 = sc.nextInt(); int x2 = sc.nextInt(); int y2 = sc.nextInt(); int res = s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]; System.out.println(res); }
差分問題:
首先明白差分的概念:差分其實就是前綴和的逆運算
差分的作用:如果對某個區間需要每個元素加上C則需要使用差分來減少時間復雜度
給定a[1],a[2]....a[n];構造擦划分數組b[N];使得a[i] = b[i]+b[2]+....+b[i];
差分的重點是:構造臨時數組b[]
b[1] = a[1]
b[2] = a[2] - a[1]
b[3 ]= a[3] - a[2]
...
b[n] = a[n] - a[n-1]
兩個數組:a[],b[],a[]稱為b[]的前綴和,b[]稱為a[]的差分
差分的下標也是從1開始;
核心操作:
將a[L~R]全部加上C,等價於:b[L] +=C, b[R+1] -=C;
一維數組的差分問題:
-
首先初始化數組s[]
int[] b = new int[x]; for (int i = 1; i <=n ; i++) { a[i] = sc.nextInt(); } -
按照上面構造數組方式構造b[]數組(兩種方法),公式:b[i] = a[i]-a[i-1]
//構造差分數組 for (int i = 1; i <=n ; i++) { b[i] = a[i]-a[i-1]; } //插入方法:假設a[]中全部為0 則b[]也全部為0,可以執行插入操作 for(int i = 1; i <= n; i++){ insert(i,i,a[i]) }插入操作:
public static void insert(int l,int r,int c){ b[l] +=c; b[r+1] -=c; } -
將所求區間(l,r)在b[]數組划分出來並加上c,公式:b[l] +=c,b[r+1] -=c;
int l,r,c; l = sc.nextInt(); r = sc.nextInt(); c = sc.nextInt(); b[l] +=c; b[r+1] -=c;因為a[]數組是b[]數組的前綴和,b[]是a[]的差分,所以在b[]的某個區間上+c會影響的a區間上的結果

-
將差分數組轉換成原數組,也就是求差分數組的前綴和,公式:a[i] = a[i-1] +b[i] //類比於s[i]=s[i-1]+a[i]
for (int i = 1; i <=n ; i++) { a[i] = a[i-1]+b[i]; System.out.print(a[i]+" "); }
二維數組的差分問題:
記住:a[][]數組是b[][]數組的前綴和數組,那么b[][]是a[][]的差分數組
二維差分的核心也是構造差分數組b[][],使得a數組中a[i][j]是b數組左上角(1,1)到右下角(i,j)所包圍矩形元素的和;
怎么讓子矩陣中的每個元素加上c;

先定義一個函數:
public static void insert(int x1,int y1,int x2,int y2,int c){
b[x1][y1] += c;
b[x2+1][y1] -=c;
b[x1][y2+1] -=c;
b[x2+1][y2+1] +=c;
}
-
初始化原數組
a[][]for (int i = 1; i <=n; i++) { for (int j = 1; j <=m ; j++) { a[i][j] = sc.nextInt(); } } -
構造差分數組
初始化B數組從
[1][1]到[i][j]添加元素,就是將a[][]中的元素遍歷到B數組中int[][] b = new int[x][x]; for (int i = 1; i <=n; i++) { for (int j = 1; j <=m ; j++) { insert(i,j,i,j,a[i][j]); } } -
輸入矩形中需要+c的范圍(x1,y1)(x2,y2),在差分數組
b[][]中找到相應的范圍+cwhile (q-- != 0){ int x1,y1,x2,y2; x1 = sc.nextInt(); y1 = sc.nextInt(); x2 = sc.nextInt(); y2 = sc.nextInt(); int c = sc.nextInt(); insert(x1,y1,x2,y2,c); } -
求
b[][]數組中的前綴和-->a[][];公式:b[i][j]=a[i][j]−a[i−1][j]−a[i][j−1]+a[i−1][j−1]for (int i = 1; i <=n ; i++) { for (int j = 1; j <=m ; j++) { b[i][j] = b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1] + b[i][j]; System.out.print(b[i][j]+" "); } } -
直接輸出
b[][]中的元素就是a[][]數組中范圍所需要+c的結果
結束:
感謝各位能看到最后
