差分模板

145 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情

一维差分

概念

image.png

  1. 构造完成以后:a数组是b数组的前缀和
  2. b称为a的差分,a称为b的前缀和
  3. 假定a数组初始的时候全部都是0,然后在对应的区间加上对应的a[i]即可
    1. image.png

作用

  1. 假设已经构造出来B数组了,如果想求A数组,则对B数组求一遍前缀和即可,时间复杂度:O(n)
  2. 给定一个区间[l, r],在A数组里面在[l, r]区间的所有元素都+c,如果循环来做,时间复杂度O(n),但是用差分可以做到O(1),如果上面的这个操作完成以后,我们想求一下原来的A数组,用B数组来求一下前缀和(对B数组扫描一边)即可得到原来的A数组,时间复杂度:O(n)
    1. image.png
    2. b[l] + ca[l]~a[n]全部都加上c了,所以要打个补丁,b[r + 1] - c,这样就可以实现上面这个需求了

题目

image.png

#include <iostream>

using namespace std;

const int N = 100010;

int n, m; 
int a[N], b[N]; // a:原数组  b:差分数组

// 插入函数,a[l ~ r] + c
void insert(int l, int r, int c)
{
    b[l] += c;
    b[r + 1] -= c;
    
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    
    // 1. 将原数组的每个数都插入进去
    for (int i = 1; i <= n; i++) insert(i, i, a[i]);
    
    // m个操作
    while (m--)
    {
        int l, r, c;
        scanf("%d%d%d", &l, &r, &c);
        insert(l, r, c);
    }
    
    // 求一下原来的数组的值,求一遍前缀和就行了
    for (int i = 1; i <= n; i++) b[i] += b[i - 1];
    
    // 将数组输出
    for (int i = 1; i <= n; i++) printf("%d ", b[i]);
    
    return 0;
}

二维差分

概念

image.png

  1. 构造一个b矩阵
    1. b数组是假设出来的
    2. b数组的性质:使得a矩阵是b矩阵的前缀和
  2. 差分都不需要考虑构造,一开始让a,b数组都为0,然后将a中的每个元素一个个插进去就行了
    1. 插入a数组的每个元素的例子:image.png
  3. 差分只需要考虑如何去更新即可
  4. 一维差分给其中的一个元素加上一个值
    1. 类比:二维差分给其中一个矩阵加上一个值即可

题目

image.png image.png

代码

#include <iostream>

using namespace std;

const int N = 1010;

int n, m, q;	// 矩阵的长、宽、操作个数
int a[N][N], b[N][N]; // a是原矩阵, b是差分矩阵

// 插入函数
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()
{
    scanf("%d%d%d", &n, &m, &q);
    
    // 读入数据
    for (int i = 1; i <= n; i++)
    	for (int j = 1; j <= m; j++)
    		scanf("%d", &a[i][j]);
    
    // 假定a数组是空的,然后再插入元素
    for (int i = 1; i <= n; i++)
    	for (int j = 1; j <= m; 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++)
    		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++) printf("%d ", b[i][j]);
        puts("");
    }
    
    return 0;
    	
}

核心:

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;
}