差分

差分就是前缀和的逆运算

一维差分

有一个数组a1,a2,a3...

构造b1,b2,b3...

使得ai=b1+b2+...+bi.

构造方法也很简单:

  • b1=a1
  • b2=a2a1
  • b3=a3a2
  • ...
  • bn=anan1

其实就是假想了一个b数组,使得a数组是它的前缀和。

比如我们想要a数组[l,r]区间内的数都加上c

那么只需要bl+c 并且br+1c,这样求前缀和得到的a数组就可以在[l,r]区间都加上了c

这样我们只需要在O(1)的时间就可以在原数组某一个区间内加上固定的值,并且通过对b数据求前缀和就可以在O(n)的时间内得到a数组

实际构造的时候我们可以假定a数组初始全部为0,则差分b数组也全部为0

我们可以将给定的值看作进行了n次插入操作,第一次让[1,1]区间加上a1,第二次是让[2,2]加上a2...

这样差分就不需要自己进行构造,想他是怎么算出来的了。

应用题目

输入一个长度为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] + " ");
    }

}

二维差分

原矩阵aij,构造差分矩阵bij,满足原矩阵是差分矩阵的前缀和就可以

一维差分是每次给一段区间加上一个值,那么二维差分就是给其中的一个子矩阵加上一个值。

比方说要给红色区域都加上一个值,如下图:

  • bx1 y1+=c,则这个点右下角所有部分都会加c,即蓝色框框部分。
  • 由于非红色部分不需要加c,所以我们需要将阴影部分减c,操作如下:
  • 将紫色区域部分都减去c,bx2+1  y1=c ,
  • 将绿色部分也减去c,bx1 y2+1=c ,
  • 最后加上减去了2遍的紫绿相交部分,bx2+1  y2+1+=c ,

初始化:

  • 假设ai j=0,则bi j也为0
  • ai 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 @   晓尘  阅读(284)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示