• 前缀和与差分


    目录

    一、前缀和

    二、差分

    1:一维差分

    (1)一维差分的概念

    (2)一维差分的代码展现

    2:二维差分

    (1)二维差分的概念与二维前缀和

    (2)二维差分的实现​​​​​​​


    一、前缀和

    什么是前缀和呢?

    在一个数组中,a[0]~a[i]它的前缀和sum[i]=a[0]+...+a[i]。很显然我们可以以得出a[i]=sum[i]-sum[i-1]。这就是前缀和的大致思想

    二、差分

    1:一维差分

    (1)一维差分的概念

    定义一个差分数组d[i],它的定义是d[i]=a[i]-a[i-1]。易得出差分是前缀和逆运算。那么差分有什么应用呢?

    比如说给定一个数组,一开始是有初值的,现在在一定的区间进行m次修改,可能加,可能减,求最后得出的数组值。你如果一个一个操作,毫无疑问数据范围大的肯定超时,这时候就要用到差分知识了。假如在[l,r]区间加1,那么该如何操作呢,应该是d[l]+=1,d[r+1]-=1。

    为什么是这样子呢?因为d[i]是差分数组,所以我们知道a[i]=a[i-1]+d[i],也就是说这里的a[i]就相当于是d[i]的前缀和,换句话说a[i]=d[1]+d[2]+d[3]+...+d[i],在[l,r]区间进行修改,d[l]+=1,d[r+1]-=1,那么在区间l前其实并未修改,而在l后却修改了,但是r+1却又将r后面的区间又抵消了。具体我们看下代码的展现吧。

    (2)一维差分的代码展现

    1. #include
    2. using namespace std;
    3. const int N=1e3+10;
    4. int a[N],d[N];
    5. int n,m,l,r,q;
    6. int main()
    7. {
    8. cin>>n>>m;
    9. for(int i=1;i<=n;i++){
    10. cin>>a[i];
    11. d[i]=a[i]-a[i-1];
    12. }
    13. while(m--){
    14. cin>>l>>r>>q;
    15. d[l]+=q;
    16. d[r+1]-=q;
    17. }
    18. for(int i=1;i<=n;i++){
    19. a[i]=a[i-1]+d[i];
    20. cout<" ";
    21. }
    22. return 0;
    23. }
    24. /*
    25. 5 3
    26. 1 2 3 4 5
    27. 1 1 3
    28. 1 2 1
    29. 2 2 4
    30. */

    2:二维差分

    (1)二维差分的概念与二维前缀和

    一维差分是好,但也是有局限性的,比如说上面的只是一次查询操作,但如果是多次呢并且查询是单点查询和区间查询呢。这个后面的树状数组以及线段树可以解决,偏题了!!!

    比如说这样一道题目吧。

    描述

    给你一个 n 行 m 列的矩阵 A ,下标从1开始。

    接下来有 q 次查询,每次查询输入 4 个参数 x1 , y1 , x2 , y2

    请输出以 (x1, y1) 为左上角 , (x2,y2) 为右下角的子矩阵的和,

    输入描述:

    第一行包含三个整数n,m,q.

    接下来n行,每行m个整数,代表矩阵的元素

    接下来q行,每行4个整数x1, y1, x2, y2,分别代表这次查询的参数

    1 \le n, m \le 10001≤n,m≤1000
    1 \le q \le 10^51≤q≤105
    -10^9 \le a[i][j] \le 10^9−109≤a[i][j]≤109
    1 \le x_1 \le x_2 \le n1≤x1​≤x2​≤n
    1 \le y_1 \le y_2 \le m1≤y1​≤y2​≤m

    输出描述:

    输出q行,每行表示查询结果。

    题解:如果这样子的题目你觉得还能用一维数组吗?很明显不能,同时你要是一个一个求,这个复杂度我就不多说了吧,过不了。那么就需要用到我们的二维差分了。与一维差分一样,它也有一个差分数组d[i][j],表示的是d[i][j]=a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1](大家可以画一个二维坐标感受一下,挺好懂的),那么求的话可以分为直接求前缀和和二维差分前缀和。偏题了,写看下该题怎么写吧。代码如下:

    1. #include
    2. using namespace std;
    3. typedef long long ll;
    4. ll n,m,q;
    5. ll a[1010][1010];
    6. int main()
    7. {
    8. ios::sync_with_stdio(-1);
    9. cin>>n>>m>>q;
    10. for(ll i=1;i<=n;i++){
    11. for(ll j=1;j<=m;j++){
    12. cin>>a[i][j];
    13. }
    14. }
    15. for(int i=1;i<=n;i++){
    16. for(int j=1;j<=m;j++){
    17. a[i][j+1]+=a[i][j];
    18. }
    19. }
    20. for(int j=1;j<=m;j++){
    21. for(int i=1;i<=n;i++){
    22. a[i+1][j]+=a[i][j];
    23. }
    24. }
    25. while(q--){
    26. //TODO
    27. int x1,y1,x2,y2;
    28. cin>>x1>>y1>>x2>>y2;
    29. cout<-1]-a[x1-1][y2]+a[x1-1][y1-1]<
    30. }
    31. return 0;
    32. }

    (2)二维差分的实现

    在二维空间中如果对指定的区域进行修改,该如何做呢?与一维数组,一样,创建一个二维差分数组即可,它的公式就是(1)点上面的,其实也都是模板滴啦,我们来看一道模板题,大家就知道了。

    描述

    给你一个n行m列的矩阵,下标从1开始。
    接下来有q次操作,每次操作输入5个参数x1, y1, x2, y2, k
    表示把以(x1, y1)为左上角,(x2,y2)为右下角的子矩阵的每个元素都加上k,
    请输出操作后的矩阵。

     

    输入描述:

    第一行包含三个整数n,m,q.
    接下来n行,每行m个整数,代表矩阵的元素

    接下来q行,每行5个整数x1, y1, x2, y2, k,分别代表这次操作的参数

    1\le n,m \le 10001≤n,m≤1000
    1\le q \le 10^51≤q≤105
    1\le x1 \le x2 \le n1≤x1≤x2≤n
    1\le y1 \le y2 \le m1≤y1≤y2≤m
    -10^9 \le 矩阵中的元素 \le 10^9−109≤矩阵中的元素≤109

    输出描述:

    输出n行,每行m个数,每个数用空格分开,表示这个矩阵。

    示例1

    输入:

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

    复制输出:

    9 8 6
    8 7 5
    1. #include
    2. using namespace std;
    3. typedef long long ll;
    4. const int N=1e3+10;
    5. ll a[N][N],d[N][N];
    6. ll n,m,q;
    7. int main()
    8. {
    9. ios::sync_with_stdio(-1);
    10. cin>>n>>m>>q;
    11. for(int i=1;i<=n;i++){
    12. for(int j=1;j<=m;j++){
    13. cin>>a[i][j];
    14. d[i][j]=a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1];
    15. }
    16. }
    17. while(q--){
    18. ll x1,y1,x2,y2,k;
    19. cin>>x1>>y1>>x2>>y2>>k;
    20. d[x2+1][y2+1]+=k;
    21. d[x1][y1]+=k;
    22. d[x1][y2+1]-=k;
    23. d[x2+1][y1]-=k;
    24. }
    25. for(int i=1;i<=n;i++){
    26. for(int j=1;j<=m;j++){
    27. a[i][j]=a[i-1][j]+a[i][j-1]+d[i][j]-a[i-1][j-1];
    28. cout<" ";
    29. }
    30. cout<
    31. }
    32. return 0;
    33. }

  • 相关阅读:
    rsync 远程同步
    网站内容下载软件有哪些 网站内容下载软件推荐 网站内容下载软件安全吗 idm是啥软件 idm网络下载免费
    上周热点回顾(5.8-5.14)
    _jb_pytest_runner.py: error: unrecognized arguments: --cov报错
    electron初学
    [Android]从app的trace打桩原理回顾zygote的fork
    狂神说MybatisPlus学习笔记
    事务 还有这些用法,之前都不知道
    RabbitMQ工作队列
    JWT靶场通关(3关)
  • 原文地址:https://blog.csdn.net/gaoqiandr/article/details/127664437