基础算法——前缀和与差分

前缀和与差分(持续更新中~)

目录

  • 一维前缀和
  • 二维前缀和
  • 一维差分
  • 二维差分

前言

  1. 差分与前缀和联系比较紧密,如一个数组x的差分数组的前缀和数组等于x本身。博主在解题过程多次遇到这个知识点,作为基础算法之一,这个知识点要求重点掌握。
  2. 一个看法:前缀和数组单个元素代表是原数组左边或左上所有元素之和、单个差分数组元素修改是修改原数组右边或右下方所有元素的值.(均包含当前元素本身)

1 一维前缀和

  • 主要功能:
    1.快速查询任意连续区间元素之和
  • 时间复杂度:
    构建o(n)、查询o(1)
  • 基本思路:
    1.利用原数组构建一个前缀和数组:s[i] = s[i - 1] + a[i]
    2.查询[l, r]区间元素和时答案为:s[r] - s[l - 1]
  • 注意事项:
    1.因为可能要用到s[最小下标-1](构建s[最小下标]时,查询区间左端点为原数组起点时),所以s数组的下标最好从1开始,为了一一对应,原数组的下标最好也从1开始。
  • 经典例题:
    1.模板题:AcWing 795. 前缀和
//模板代码示例
#include<iostream>

using namespace std;

const int N = 1e5 + 5;

int s[N];

int main()
{
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
        cin >> s[i];	//该题原数组无需保存
        s[i] += s[i - 1];	//构建前缀和数组,
    }
    while(m--)
    {
        int l, r;
        cin >> l >> r;
        cout << s[r] - s[l - 1] << endl;	//区间元素之和查询
    }
    return 0;
}


2 二维前缀和

  • 主要功能:
    1.快速查询任意子矩阵元素之和
  • 时间复杂度:
    构建o(n2)、查询o(1)
  • 基本思路:
    1.构建二维前缀和数组:s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j]
    2.查询以(x1,y1)为左上角端点,(x2,y2)为右下角端点的子矩阵之和:ans = s[x2][y2] - s[x1-1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]
  • 注意事项:
    1.注意边界问题
    2.原数组和前缀和数组的下标最好从1开始
  • 经典例题:
    1.模板题:AcWing 796. 子矩阵的和
  • 拓展练习:
    1.Acwing99.激光炸弹
    2.Acwing1230.K倍区间
//模板代码示例
#include<iostream>

using namespace std;

const int N = 1005;

int a[N][N], s[N][N], n, m, q;
//查询子矩阵和函数
int query(int x1, int y1, int x2, int y2)
{
    return s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1];
}
int main()
{
    cin >> n >> m >> q;
    //输入原数组
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            cin >> a[i][j];
    //构建前缀和数组
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            s[i][j] = a[i][j] + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
    
    while(q--)
    {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        cout << query(x1, y1, x2, y2) << endl;
    }
    return 0;
}

3 一维差分

  • 主要功能:
    1.快速对任意连续区间所有元素加减一个数x
  • 时间复杂度:
    构建o(n),修改o(1),还原o(n)
  • 基本思路:
    1.构建差分数组:b[i] = a[i] - a[i - 1](类比一维前缀和数组构建公式,只是a[]移到等号左边,所有s[]移到等号右边)
    2.修改区间[l,r],使所有元素加上x:b[l] += x, b[r + 1] -= x
    3.将差分数组还原为原数组a[]:a[i] = a[i - 1] + b[i](和一维前缀和数组的构建公式原理一样)
  • 注意事项:
    1.下标最好从1开始
  • 经典例题:
    1.模板题:AcWing 797. 差分
  • 拓展练习
    1.差分数组性质:Acwing100. 增减序列
    2.区间修改:Acwing101.最高的牛
//模板代码示例
#include<iostream>

using namespace std;

const int N = 1e5 + 5;

int a[N], b[N], n, m;

int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
        b[i] = a[i] - a[i - 1];	//构建差分数组
    }
    while(m--)
    {
        int l, r, c;
        cin >> l >> r >> c;
        b[r + 1] -= c;	//区间修改
        b[l] += c;
    }
    for(int i = 1; i <= n; i++)
        b[i] += b[i - 1];	//将差分数组还原为原数组
    for(int i = 1; i <= n; i++)
        cout << b[i] << " ";
    return 0;
}

4 二维差分

  • 功能:
    1.快速对一个子矩阵所有元素加减一个数x
  • 时间复杂度:
    构建o(n2), 修改o(1), 还原o(n2
  • 基本思路:
    1.构建二维差分矩阵:b[i][j] = a[i][j] - a[i - 1][j] - a[i][j - 1] + a[i - 1][j - 1](类比于二维前缀和数组的构建公式,只是将a[]移到等式左边,将所有s[]移到右边)
    2.(x1,y1)和(x2,y2)为左上和右下端点的子矩阵所有元素加x:b[x1][y1]+=x, b[x1][y2+1]-=x, b[x2+1][y1]-=x, b[x2+1][y2+1]+=x
    3.还原:a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + b[i][j](类比二维前缀和构建公式)
  • 注意事项
    1.下标从1开始
  • 经典例题:
    1.模板题:AcWing 798. 差分矩阵
#include<iostream>

using namespace std;

const int N = 1e3 + 5;

int a[N][N], b[N][N];
int n, m, q;

int main()
{
    cin >> n >> m >> q;
    //构建差分矩阵
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
        {
            cin >> a[i][j];
            b[i][j] = a[i][j] - a[i - 1][j] - a[i][j - 1] + a[i - 1][j - 1];
        }
     //对子矩阵元素加c
    while(q--)
    {
        int x1, x2, y1, y2, c;
        cin >> x1 >> y1 >> x2 >> y2 >> c;
        b[x1][y1] += c;
        b[x1][y2 + 1] -= c;
        b[x2 + 1][y1] -= c;
        b[x2 + 1][y2 + 1] += 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];
    //输出答案
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= m; j++)
            cout << a[i][j] << " ";
        cout << endl;
    }
    return 0;
}

PS:文章如有问题恳请各位大佬帮忙指正,本文章会持续不定期更新中~

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值