前缀和
什么是前缀和?
给定一个元素个数为n的数组A,数组元素为a1``, a2``, ... , an。
前缀和是这样一个数组,其元素满足s1 = a1s2 = a1 + a2s3 = a1 + a2 + a3...sn = a1 + a2 + ... + an
也就是说s1 = s0+a1s2 = s1 + a2s3 = s2 + a3...sn = s(n-1) + an
注意:为了避免下标的转换,使原数组A下标从1开始计数。
前缀和的用处
给定原数组A,求A的某一段序列的和。
注意:两种给定序列区间的说法:
- 起始下标
i和结束下标j(都是闭区间) - 求数组A中第
l个元素至第r个元素的和(闭区间)
我们的前缀和数组直接对应第二种说法:result = s[r] - s[l-1]
如果是第一种说法,需要转换成第二种,方便理解。即l = i + 1, r = j + 1; result = s[r] - s[l-1]
一维数组前缀和
例题:
| Acwing 795. 前缀和 | 数组 | 前缀和 |
|---|---|---|
输入一个长度为 n 的整数序列。
接下来再输入 m 个询问,每个询问输入一对 l,r。
对于每个询问,输出原序列中从第 l 个数到第 r 个数的和。
输入格式
第一行包含两个整数 n 和 m。
第二行包含 n 个整数,表示整数数列。
接下来 m行,每行包含两个整数 l 和 r,表示一个询问的区间范围。
输出格式
共 m 行,每行输出一个询问的结果。
数据范围
1≤l≤r≤n,
1≤n,m≤100000,
−1000≤数列中元素的值≤1000
输入样例:
5 32 1 3 6 41 21 32 4
输出样例:
3610
import java.util.*;public class Main {static final int N = 100010;static int[] pre = new int[N];static int n, m;public static void main(String[] args) {Scanner sc = new Scanner(System.in);n = sc.nextInt();m = sc.nextInt();for (int i = 1; i <= n; i++) {pre[i] = sc.nextInt();pre[i] += pre[i - 1];}while (m-- > 0) {int l = sc.nextInt(), r = sc.nextInt();int res = query(l, r);System.out.println(res);}}static int query(int l, int r) {return pre[r] - pre[l - 1];}}
二维数组前缀和

例题:
| AcWing 796. 子矩阵的和 | 数组 | 前缀和 |
|---|---|---|
输入一个 n 行 m 列的整数矩阵,再输入 q 个询问,每个询问包含四个整数 x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。
对于每个询问输出子矩阵中所有数的和。
输入格式
第一行包含三个整数 n,m,q。
接下来 n 行,每行包含 m 个整数,表示整数矩阵。
接下来 q 行,每行包含四个整数 x1,y1,x2,y2,表示一组询问。
输出格式
共 q 行,每行输出一个询问的结果。
数据范围
1≤n,m≤1000,
1≤q≤200000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
−1000≤矩阵内元素的值≤1000
输入样例:
3 4 31 7 2 43 6 2 82 1 2 31 1 2 22 1 3 41 3 3 4
输出样例:
172721
import java.util.*;public class Main {static final int N = 1010;static int[][] pre = new int[N][N];static int n, m, q;public static void main(String[] args) {Scanner sc = new Scanner(System.in);n = sc.nextInt();m = sc.nextInt();q = sc.nextInt();for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {int x = sc.nextInt();pre[i][j] = x + pre[i - 1][j] + pre[i][j - 1] - pre[i - 1][j - 1];}}while (q-- > 0) {int x1 = sc.nextInt(), y1 = sc.nextInt();int x2 = sc.nextInt(), y2 = sc.nextInt();int res = query(x1, y1, x2, y2);System.out.println(res);}}static int query(int x1, int y1, int x2, int y2) {return pre[x2][y2] - pre[x2][y1 - 1] - pre[x1 - 1][y2] + pre[x1 - 1][y1 - 1];}}
差分
什么是差分?
差分是前缀和的逆运算,对于数组a和b来说,假设a是原数组,b是a数组的差分数组,那么对于b来说,a数组就是b数组的前缀和数组。
如果定义前缀和为函数f,有a = f(b), b = f``-1``(a)
差分的用处
如果想对数组a的一个子序列进行整体操作,使得该子序列的每个元素都+c,如果不用差分来做,每次操作时间复杂度都为O(n),而使用差分数组b来做,只需要一次初始化O(n)操作,其它对子序列的整体操作时间复杂度都为O(1)
拥有数组b[n]后,想要对a数组中所有的数据加上c,只需要将b[1]+c即可,因为a[i]是b[i]的前缀和,a[i]=b[1]+b[1]+b[3]+……+b[n]。
如果想将a数组中[l,r]部分的数据全部加上c,只需要将b[l]+c,然后b[r+1]-c即可。
差分数组的构造也可以使用上述思想,即[l, r] 变成[i,i]即可。
注意:同前缀和一样,数组都是从1开始的,如果原数组的个数为n,那么差分数组的长度为n+2,这样做的好处是方便边界处理(前后各一个)。
一维差分
| Acwing 797. 差分 | 数组 | 差分 |
|---|---|---|
输入一个长度为n的整数序列。
接下来输入m个操作,每个操作包含三个整数l, r, c,表示将序列中[l, r]之间的每个数加上c。
请你输出进行完所有操作后的序列。
输入格式
第一行包含两个整数n和m。
第二行包含n个整数,表示整数序列。
接下来m行,每行包含三个整数l,r,c,表示一个操作。
输出格式
共一行,包含n个整数,表示最终序列。
数据范围
1≤n,m≤100000,
1≤l≤r≤n,
−1000≤c≤1000,
−1000≤整数序列中元素的值≤1000
输入样例:
6 31 2 2 1 2 11 3 13 5 11 6 1
输出样例:
3 4 5 3 4 2
import java.util.*;public class Main {static final int N = 100010;static int[] diff = new int[N];static int n, m;public static void main(String[] args) {Scanner sc = new Scanner(System.in);n = sc.nextInt();m = sc.nextInt();for (int i = 1; i <= n; i++) {int x = sc.nextInt();update(i, i, x);}for (int i = 0; i < m; i++) {int l = sc.nextInt(), r = sc.nextInt(), x = sc.nextInt();update(l, r, x);}for (int i = 1; i <= n; i++)diff[i] += diff[i - 1];for (int i = 1; i <= n; i++)System.out.print(diff[i] + " ");}static void update(int l, int r, int x) {diff[l] += x;diff[r + 1] -= x;}}
二维差分

例题:
| Acwing 798. 差分矩阵 | 数组 | 差分 |
|---|---|---|
输入一个n行m列的整数矩阵,再输入q个操作,每个操作包含五个整数x1, y1, x2, y2, c,其中(x1, y1)和(x2, y2)表示一个子矩阵的左上角坐标和右下角坐标。
每个操作都要将选中的子矩阵中的每个元素的值加上c。
请你将进行完所有操作后的矩阵输出。
输入格式
第一行包含整数n,m,q。
接下来n行,每行包含m个整数,表示整数矩阵。
接下来q行,每行包含5个整数x1, y1, x2, y2, c,表示一个操作。
输出格式
共 n 行,每行 m 个整数,表示所有操作进行完毕后的最终矩阵。
数据范围
1≤n,m≤1000,
1≤q≤100000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
−1000≤c≤1000,
−1000≤矩阵内元素的值≤1000
输入样例:
3 4 31 2 2 13 2 2 11 1 1 11 1 2 2 11 3 2 3 23 1 3 4 1
输出样例:
2 3 4 14 3 4 12 2 2 2
import java.util.*;import java.io.*;public class Main {static final int N = 1010;static int[][] a = new int[N][N];static int n, m, q;public static void main(String[] args) throws IOException {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));String[] ss = br.readLine().split(" ");n = Integer.parseInt(ss[0]);m = Integer.parseInt(ss[1]);q = Integer.parseInt(ss[2]);for (int i = 1; i <= n; i++) {ss = br.readLine().split(" ");for (int j = 1; j <= m; j++) {int x = Integer.parseInt(ss[j - 1]);add(i, j, i, j, x);}}while (q-- > 0) {ss = br.readLine().split(" ");int x1 = Integer.parseInt(ss[0]), y1 = Integer.parseInt(ss[1]);int x2 = Integer.parseInt(ss[2]), y2 = Integer.parseInt(ss[3]);int v = Integer.parseInt(ss[4]);add(x1, y1, x2, y2, v);}for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {a[i][j] += a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];bw.write(a[i][j] + " ");}bw.write("\n");}bw.close();br.close();}static void add(int x1, int y1, int x2, int y2, int c) {a[x1][y1] += c;a[x1][y2 + 1] -= c;a[x2 + 1][y1] -= c;a[x2 + 1][y2 + 1] += c;}}
三维差分
其它例题
798. 得分最高的最小轮调
class Solution {public int bestRotation(int[] nums) {int n = nums.length;int[] diff = new int[n + 1];for (int i = 0; i < n; i++) {if (nums[i] <= i) { // 第一类diff[0] += 1;diff[i - nums[i] + 1] -= 1;diff[i + 1] += 1;} else { //第二类diff[0] += 0;diff[i + 1] += 1;diff[i + 1 + n - nums[i]] -= 1;}}int minIdx = 0, score = 0;int sum = 0;for (int i = 0; i < n; i++) {sum += diff[i];if (sum > score) {score = sum;minIdx = i;}}return minIdx;}}
