差分
差分就是前缀和的逆运算
一维差分
有一个数组\(a_1,a_2,a_3...\)
构造\(b_1,b_2,b_3...\)
使得\(a_i = b_1 + b_2 + ... + b_i\).
构造方法也很简单:
- \(b_1 = a_1\)
- \(b_2 = a_2 - a_1\)
- \(b_3 = a_3 - a_2\)
- ...
- \(b_n = a_n - a_{n-1}\)
其实就是假想了一个b数组,使得a数组是它的前缀和。
比如我们想要a数组[l,r]区间内的数都加上c
那么只需要\(b_{l+c}\) 并且\(b_{r+1}-c\),这样求前缀和得到的a数组就可以在[l,r]区间都加上了c
这样我们只需要在O(1)的时间就可以在原数组某一个区间内加上固定的值,并且通过对b数据求前缀和就可以在O(n)的时间内得到a数组
实际构造的时候我们可以假定a数组初始全部为0,则差分b数组也全部为0
我们可以将给定的值看作进行了n次插入操作,第一次让[1,1]区间加上\(a_1\),第二次是让[2,2]加上\(a_2\)...
这样差分就不需要自己进行构造,想他是怎么算出来的了。
应用题目
输入一个长度为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 3 1 2 2 1 2 1 1 3 1 3 5 1 1 6 1输出样例
3 4 5 3 4 2
代码
import java.util.*;
public class Main{
static Scanner scanner = new Scanner(System.in);
static final int N = 10010;
static int n = scanner.nextInt();
static int m = scanner.nextInt();
static int[] a = new int[N];
static int[] b = new int[N];
public static void insert(int l, int r, int c)
{
b[l] += c;
b[r+1] -= c;
}
public static void main(String args[]) throws Exception {
for(int i = 1; i <= n; i++)
a[i] = scanner.nextInt();
for(int i = 1; i <= n; i++)
insert(i,i,a[i]);
while(m-- > 0)
{
int l=scanner.nextInt();
int r=scanner.nextInt();
int c=scanner.nextInt();
insert(l,r,c);
}
for(int i = 1; i <= n; i++)
b[i] += b[i-1];
for(int i = 1; i <= n; i++)
System.out.print(b[i] + " ");
}
}
二维差分
原矩阵\(a_{ij}\),构造差分矩阵\(b_{ij}\),满足原矩阵是差分矩阵的前缀和就可以
一维差分是每次给一段区间加上一个值,那么二维差分就是给其中的一个子矩阵加上一个值。
比方说要给红色区域都加上一个值,如下图:

- 令\(b_{x_1 \ y_1} += c\),则这个点右下角所有部分都会加c,即蓝色框框部分。
- 由于非红色部分不需要加c,所以我们需要将阴影部分减c,操作如下:
- 将紫色区域部分都减去c,\(b_{x_{2+1 \ } \ y_1} -= c\) ,
- 将绿色部分也减去c,\(b_{x_1 \ y_{2+1}} -= c\) ,
- 最后加上减去了2遍的紫绿相交部分,\(b_{x_{2+1} \ \ y_{2+1}} += c\) ,
初始化:
- 假设\(a_{i \ j} = 0\),则\(b_{i \ j}\)也为0
- 将\(a_{i \ j}\)的元素依次插入就可以了,看作左上角是[i,j],右下角也是[i,j]就可以了
例题
输入一个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 3 1 2 2 1 3 2 2 1 1 1 1 1 1 1 2 2 1 1 3 2 3 2 3 1 3 4 1输出样例
2 3 4 1 4 3 4 1 2 2 2 2
代码
import java.util.*;
public class Main{
static Scanner scanner = new Scanner(System.in);
static final int N = 1010;
static int n = scanner.nextInt();
static int m = scanner.nextInt();
static int q = scanner.nextInt();
static int[][] a = new int[N][N];
static int[][] b = new int[N][N];
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;
}
public static void main(String args[]) throws Exception {
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
a[i][j] = scanner.nextInt();
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
insert(i,j,i,j,a[i][j]);
while(q-- > 0)
{
int x1=scanner.nextInt();
int y1=scanner.nextInt();
int x2=scanner.nextInt();
int y2=scanner.nextInt();
int c=scanner.nextInt();
insert(x1,y1,x2,y2,c);
}
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];
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++) System.out.print(b[i][j] + " ");
System.out.println();
}
}
}

浙公网安备 33010602011771号