差分

差分就是前缀和的逆运算

一维差分

有一个数组\(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();
        }
    }

}
posted @ 2022-06-13 16:08  晓尘  阅读(299)  评论(0)    收藏  举报