差分:一维+二维

一维

对于一个数列 a_{i},我们需要维护的数据是“相邻两个数之差”。这种策略是,令p_{i}=\bigtriangleup a_{i} = a_{i}-a_{i-1},即相邻两数的差。我们称数列 p_{i} 为数列 a_{i} 的差分数列。

它可以维护多次对序列的一个区间加上一个数,并在最后询问某一位的数或是多次询问某一位的数。譬如使 [l, r] 每个数加上一个 k,就是 p_{l}\leftarrow p_{l}+k, p_{r+1}\leftarrow p_{r+1}-k,最后做一遍前缀和。

p [i] = p [i] +p [ i−1]

就是对这个差分数列 p_{i} 做一遍前缀和就得到了原来的数列 a_{i} ,即 a_{i} 相当于 p_{[1,\ i]} 这个前缀和。

极大的缩减了时间复杂度,时间复杂度就是 O(n)。

AC代码

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5+6;
int s[MAXN] = {};
int p[MAXN] = {};
 
int main() {
    int n,m;
    scanf("%d%d", &n, &m);
    int i;
    for (i=1; i<=n; i++) {
        scanf("%d", &s[i]);
        p[i] = s[i] - s[i-1];
    }
    for (i=0; i<m; i++) {
        int l, r, c;
        scanf("%d%d%d", &l, &r, &c);
        p[l] += c;
        p[r+1] -= c;
    }
    //输出
    int ans=p[0];
    for (i=1; i<=n; i++) {
        ans += p[i];
        printf("%d ", ans);
    }
    printf("\n");
    return 0;
}

二维

题目描述
输入一个 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 个整数,表示所有操作进行完毕后的最终矩阵。

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

p[i][j]=a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1]

如何从差分矩阵得到原矩阵呢?可以参考下面公式

a[i][j] = p[i][j]+p[i-1][j]+p[i][j-1]-p[i-1][j-1]

类比一维可得出每次输入5 个整数 x1,y1,x2,y2,c,表示一个操作。

  1. p[x1][y1] += c;

  2. p[x1][y2+1] -=c;

  3. p[x2+1][y1] -=c;

  4. p[x2+1][y2+1] += c;

AC代码

#include <bits/stdc++.h>
using namespace std;
int a[1005][1005] ={0};
int p[1005][1005] ={0};
int main() {
    int n,m,q;
    scanf("%d%d%d", &n, &m, &q);
    int i, j;
    for (i=1; i<=n; i++) {
        for (j=1; j<=m; j++) {
            scanf("%d", &a[i][j]);
            p[i][j] = a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1];
        }
    }
    for (i=0; i<q; i++) {
        int x1, y1, x2, y2, c;
        scanf("%d%d%d%d%d", &x1, &y1, &x2, &y2, &c);
        p[x1][y1] += c;
        p[x1][y2+1] -=c;
        p[x2+1][y1] -=c;
        p[x2+1][y2+1] += c;
    }
    for (i=1; i<=n; i++) {
        for (j=1; j<=m; j++) {
            p[i][j] += p[i-1][j]+p[i][j-1]-p[i-1][j-1];
            printf("%d ", p[i][j]);
        }
        printf("\n");
    }
 
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值