kizumi_header_banner_img

是先学算法,先刷题,还是先写日记呢?

加载中

文章导读

前缀和与差分


avatar
RonF02 2026年2月1日 70

用来降低时间复杂度的妙妙道具~
O(n)直接变成O(1)啦~~~

前缀和

一维前缀和

顾名思义,前缀和就是前缀的和。好吧我好像说了一句废话

本质上就是酱紫:
原来: a[1] , a[2] , a[3] , … a[n];
前缀和 s[i] = a[1] + a[2] + … + a[i], s[0] = 0
于是乎,只要数学合格,就可以注意力惊人地发现:
s[r] – s[l – 1] 竟然等于 a[l] + a[l + 1] + … + a[r]!
然后O(n)的时间复杂度就被我们成功地压成了O(1)

废话不多说直接贴代码

#include <bits/stdc++.h>
using namespace std;

int main()
{
    int n, m; cin >> n >> m;
    vector<int> a(n + 1), s(n + 1);
    for (int i = 1; i <= n; i ++ ) 
    {
        cin >> a[i];
        s[i] = a[i] + s[i - 1];
    }
    
    while (m --)
    {
        int l, r;
        cin >> l >> r;
        cout << s[r] - s[l - 1] << '\n';
    }
    
    return 0;
}

二维前缀和

二维前缀和就有点困难了,有点像我们小学时候学的容斥原理。
核心公式就只有两个

  1. 前缀和更新公式 s[i][j] = s[i – 1][j] + s[i][j – 1] +a[i][j] – s[i – 1][j – 1]
  2. 从(x1,y1)到(x2,y2)的矩形内的所有数的和:
    sum = s[x2][y2] – s[x2][y1 – 1] – s[x1 – 1][y2] + s[x1 – 1][y1 – 1]

看着有点难记呢,但是只要画画图,再回想一下容斥原理,还是很容易推导的

最后,当然少不了贴代码环节~

#include <bits/stdc++.h>
using namespace std;

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n, m, q; cin >> n >> m >> q;
    vector a(n + 1, vector<int> (m + 1));
    vector s(n + 1, vector<int> (m + 1));
    for (int i = 1; i <= n ; i ++ )
    {
        for (int j = 1; j <= m; j ++ )
        {
            cin >> a[i][j];
            s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
        }
    }

    while (q -- )
    {
        int x1, y1, x2, y2; 
        cin >> x1 >> y1 >> x2 >> y2;
        cout << s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1] << '\n';
    }
    return 0;
}

差分

差分就是前缀和的逆运算,类比积分和导数的关系(话说你为什么不类比加法和减法啊喂)
注意,差分数组必须开大一圈,否则会访问越界。

虽然差分相当于前缀和的逆运算,但实际上这个逆运算并没有构造的必要。具体原因后续会详细说明。有具体的例子更便于理解

作用:
给定区间 [l, r] 使得a在区间中的所有的数都加上c
如果暴力做要O(n),但是差分做只要O(1)

一维差分

首先,我们考虑有一个数组
a[1], a[2], … , a[n]
我们构造b数组,使得a[i] = b[1] + b[2] + … + b[i],则称b为a的差分,a为b的前缀和

好的,我们现在下好定义了,然后我们只要考虑如何修改b中的元素,使得a数组的值能相应地改变
其实通过差分数组只要修改两个数即可

  • b[l] + c ==> a[l] ~ a[n] 中的每一个数都加上了c
  • b[r + 1] – c ==> a[r + 1] ~ a[n] 中的每一个数都减去了c

a[r + 1] ~ a[n] 中的每个数既加上了c,又减去了c,相当于没有改变。
于是我们就仅仅修改了两个数,就让a[r + 1] ~ a[n] 中的每个数都加上了c
成功降低了时间复杂度

最后不要忘记用前缀和加回来

直接贴代码

#include <bits/stdc++.h>
using namespace std;

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

int main()
{
    int n, m; cin >> n >> m;
    vector<int> a(n + 1), b(n + 2);
    for (int i = 1; i <= n; i ++ ) 
    {
        cin >> a[i];
        insert(i, i, a[i], b);
    }
    
    while (m -- )
    {
        int l, r, c;
        cin >> l >> r >> c;
        insert(l, r, c, b);
    }
    
    for (int i = 1; i <= n; i ++ ) 
    {
        b[i] += b[i - 1];
        cout << b[i] << ' ';
    }
    
    cout << '\n';
    
    return 0;
}

二维差分(差分矩阵)

二维差分与一维差分相似

给子矩阵 (x1, y1) 到 (x2, y2) 加上 c 的操作如下

  • b[x1][y1] + c ==> (x1, y1) 右下角所有的点都加上c
  • b[x2 + 1][y1] – c ==> 一个长方形减去c
  • b[x1][y2 + 1] – c ==> 一个长方形减去c
  • b[x2 + 1][y2 + 1] + c ==> 正方形补上被多减去的c

与二维前缀和类似,只要结合容斥原理依然便于记忆

同样,不要忘记前缀和加回来,以及数组开到 n + 2, m + 2

#include <bits/stdc++.h>
using namespace std;

void insert(int x1, int y1, int x2, int y2, int c, vector<vector<int>> &b)
{
    b[x1][y1] += c;
    b[x2 + 1][y1] -= c;
    b[x1][y2 + 1] -= c;
    b[x2 + 1][y2 + 1] += c;
}

int main()
{
    int n, m, q; cin >> n >> m >> q;
    vector a(n + 2, vector<int> (m + 2));
    for (int i = 1; i <= n; i ++ )
    {
        for (int j = 1; j <= m; j ++ )
        {
            int tmp; cin >> tmp;
            insert(i, j, i, j, tmp, a);
        }
    }

    while(q -- )
    {
        int x1, y1, x2, y2, c;
        cin >> x1 >> y1 >> x2 >> y2 >> c;
        insert(x1, y1, x2, y2, c, a);
    }

    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];
            cout << a[i][j] << ' ';
        }
        cout << '\n';
    }

    return 0;
}

写在最后

本来以为差分会是很难理解的东西,实际上手以后发现其实并不困难,只要结合容斥原理非常便于记忆。还有,踩过的坑不要再踩,喜欢开vector的我一定要把数组开大,开大!



评论(0)

查看评论列表

暂无评论


发表评论

表情 颜文字
插入代码
RonF02的博客

个人信息

avatar

24
文章
2
评论
1
用户