You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

6.0 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

AcWing 797. 差分

定义:b[i]=a[i]-a[i-1],称b数组是a数组的差分数组。

举个栗子:

a=[0,1,2,3,4,5]

b=[0,1,1,1,1,1]

为啥呢? a[5]-a[4]=b[5] a[4]-a[3]=b[4] a[3]-a[2]=b[3] a[2]-a[1]=b[2] a[1]-a[0]=b[1]

算一下,就是b=[1,1,1,1,1]

把上面五个式子左边加在一起,右边也加在一起,两边还应该相等。就是: a[5]-a[4]+a[4]-a[3]+a[3]-a[2]+a[2]-a[1]+a[1]-a[0]=b[5]+b[4]+b[3]+b[2]+b[1]

a[5]=b[1]+b[2]+b[3]+b[4]+b[5]

太奇妙了!a数组是b数组的一维前缀和数组!

小结: (1)、a数组中,每个数字后面减前面得到的数字填入b数组b数组就叫a数组的差分数组。

(2)、同时,a数组就是b数组的前缀和数组。

(3)、通过“叠加”差分数组,就可以还原出“原数组”的每一个数字!

(4)、前缀和与差分互为逆运算,有原数组可以计算出差分数组;有差分数组,也可以还原成原数组。

2、一维差分作用

使用场景:

在某个区间[l,r]的多次操作都加上(或减去)一个数x时,一维差分可以大大提高运算速度。

举个栗子:

总结公式:

void insert(int l,int r,int c){
    b[l]+=c;
    b[r+1]-=c;
}

我们利用刚才的结论:通过“叠加”差分数组,就可以还原出“原数组”的每一个数字!!这玩意就整一回合适吗?不合适,还不够费功夫的呢!生成一维差分也就算了,从一维差分数组还原回原数组就需要n次运算,整不好还赔了呢!但如果是多次区间加减运算,就合适了!

3、一维差分应用

#include <bits/stdc++.h>

using namespace std;
const int N = 100010;
int n, m;
int a[N], b[N];

/**
 * 功能:差分计算
 * @param l 左边界
 * @param r 右边界
 * @param c 值
 */
void insert(int l, int r, int c) {
    b[l] += c;
    b[r + 1] -= c;
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        insert(i, i, a[i]);
        //或者 b[i]=a[i]-a[i-1];
  }

    while (m--) {
        int l, r, c;
        cin >> l >> r >> c;
        insert(l, r, c);
    }

    for (int i = 1; i <= n; i++)
        a[i] = a[i - 1] + b[i], printf("%d ", a[i]);
    return 0;
}

AcWing 798. 差分矩阵

1、二维差分定义

我们有一个矩阵,如下图所示。

根据二维前缀和表示的是右上角矩形的和,由于差分只涉及前面相邻的数(由一维可以推出),并且由前面范围的数相加得到这个位置的数。那么类比二维前缀和和一维差分,可以简单推测出

二维差分的公式 b[i][j]=a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1]

如何从差分矩阵得到原矩阵呢?[就是二维前缀和公式] a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + b[i][j]

举个栗子 比如,我们有一个矩阵 a如下所示

1 2 4 3
5 1 2 4
6 3 5 9

那么对应的二维差分矩阵 b 如下:

1  1  2 -1
4 -5 -1  3
1  1  1  2

2、二维差分用途

和一维差分的用途基本一致在一个二维矩阵中有多块区间需要增加或减少一个数值多次操作后求最终的矩阵内容。如果按照传统办法就是二层循环复杂度很高如果预处理出一个二维的差分矩阵以后的多轮操作都转为了4次加加减减操作可以视为O(1)级别的时间复杂度,运算效率将得到极大提高。

3、二维差分构建

如果我们要在左上角是 (x1,y1),右下角是 (x2,y2) 的矩形区间每个值都 +c,如下图所示 1.png 在我们要的区间开始位置x1,y1+c,根据前缀和的性质,那么它影响的就是整个黄色部分,多影响了两个蓝色部分,所以在两个蓝色部分 -c 消除 +c 的影响,而两个蓝色部分重叠的绿色部分多了个 -c 的影响,所以绿色部分 +c 消除影响。所以对应的计算方法如下:

void insert(int x1, int y1, int x2, int y2, int c){
    b[x1][y1] += c;             //开始位置增加C
    b[x2 + 1][y1] -= c;         //见上面的图把第二行的蓝色区域减去C
    b[x1][y2 + 1] -= c;         //见上面的图把第一行的蓝色区域减去C
    b[x2 + 1][y2 + 1] += c;     //交叉位置被减了两次,需要补回来。
}

4、二维差分应用

#include <bits/stdc++.h>

using namespace std;
const int N = 1010;
int a[N][N], b[N][N];
int n, m, q;

/**
 * 功能:二维差分构建
 * @param x1 左上角横坐标
 * @param y1 左上角纵坐标
 * @param x2 右下角横坐标
 * @param y2 右下角纵坐标
 * @param c  值
 */
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;
}

int main() {
    cin >> n >> m >> q;
    //读入并构建
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            cin >> a[i][j], insert(i, j, i, j, a[i][j]);

    //q次区域变化
    while (q--) {
        int x1, y1, x2, y2, c;
        cin >> x1 >> y1 >> x2 >> y2 >> c;
        insert(x1, y1, x2, y2, c);
    }
    
    //还原二维数组
    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] + b[i][j];
            printf("%d ", a[i][j]);
        }
        printf("\n");
    }
    return 0;
}