前缀和 & 差分

一维前缀和

输入一个数组 arr ,构造前缀数组 pre ,pre数组第 i 位代表的是,arr 数组前 i 个元素的和。

构造前缀和:

pre[i] = pre[i-1] + arr[i]

那么,如果我们求arr数组中 从 L 到 R 范围的和,那么就可以用 pre [ R ] - pre [ L - 1 ] 。

#include <iostream>
#include <vector>

using namespace std;

int main(){
    int n,m; cin>>n>>m;
    vector<int> data;
    for(int i = 0;i<n;++i){
        int tp;
        cin>>tp;
        data.push_back(tp);
    }
    vector<int> pre(n+1,0);
    for(int i = 1;i<=n;++i){
        pre[i] = pre[i-1] + data[i-1];
    }
    while(m--){
        int l,r; cin>>l>>r;
        cout<< pre[r] - pre[l-1]<<endl;
    }
    return 0;
}

二维前缀和

与一维前缀和的思想相似,输入一个二维矩阵 arr[][] , 前缀和矩阵 pre [ i ][ j ] 代表的就是 arr 矩阵从0,0 到 i,j这么一个子矩阵的和。

构造前缀和:

pre[i][j] = pre[i-1][j] + pre[i][j-1] - pre[i-1][j-1] + arr[i][j]

如果我们想求 arr 数组从 (x1,y1) 到 (x2,y2) 子矩阵的和,就能用

        pre [x2][y2] - pre [x2][y1-1] - pre [x1-1][y2] + pre [x1-1][y1-1]  实现

#include <iostream>
#include <vector>

using namespace std;

int main(){
    int n,m,q; cin>>n>>m>>q;
    vector<vector<int>> data;
    for(int i = 0; i< n;++i){
        vector<int> tparr;
        for(int j = 0;j<m;++j){
            int tp; cin>>tp;
            tparr.push_back(tp);
        }
        data.push_back(tparr);
    }
    
    vector<vector<int>> preSum(n+1,vector<int>(m+1,0));
    for(int i = 1; i <= n; ++i)
        for(int j = 1 ; j <= m ; ++j)
            preSum[i][j] = preSum[i-1][j] + preSum[i][j-1] - preSum[i-1][j-1] + data[i-1][j-1];
    while(q--){
        int x1,y1,x2,y2;
        cin>>x1>>y1>>x2>>y2;
        cout<< preSum[x2][y2] - preSum[x2][y1-1] - preSum[x1-1][y2] + preSum[x1-1][y1-1] <<endl;
    }
    
    return 0;
}

一维差分

输入arr = [ 1 2 2 1 2 1],那么,一个差分数组dif,dif[i]代表的就是arr[i] 和 arr[i-1]的差。

其中,dif[0] = arr[0],所以,我们就可以通过对dif数组累加(前缀和)的方式,重新构造出arr。

因为是重新构造arr是累加的形式,所以,一旦对dif中的某个位置dif[i]的值进行了修改,那每个包含dif[i]的前缀和都会跟着被修改,表现在,dif[i]被修改,dif[i ~ n]都被做了相同的修改。

dif数组的构造:

dif[i] = arr[i] - arr[i-1]

如果我们希望对arr中 L 到 R 位置上的值都+c,就可以通过对dif[L]+c实现arr[L~n]的值+c,然后通过dif[R+1]-c,实现arr[ L~R ]位置上的值+c。

在代码实现中,为了方便起见,对输入数组和差分数组的大小都进行了+1,把0位置空出来。这样在构造dif矩阵的时候就不需要特判了。

#include <iostream>

using namespace std;
const int N = 1e5+10;
int dd[N], dif[N];

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

二维差分矩阵

输入arr[][] ,那么,一个差分矩阵dif[][] 代表的就是  arr[i][j] 是差分矩阵0,0 ~ i,j 子矩阵的和。

所以,我们也可以通过对dif数组求前缀和的方式,重新构造出arr。

因为是重新构造arr是累加的形式,所以,一旦对dif中的某个位置dif[i][j]的值进行了修改,那每个包含dif[i][j]的前缀和都会跟着被修改,表现在,dif[i][j]被修改,i,j ~ n,m的子矩阵都被做了相同的修改。

dif矩阵的构造:

因为arr是dif的前缀和,所以由前缀和矩阵公式,arr[i][j] = arr[i-1][j] + arr[i][j-1] - arr[i-1][j-1] + dif[i][j]

则 dif[i][j] = arr[i][j] - arr[i-1][j] - arr[i][j-1] + arr[i-1][j-1]

如果我们希望对arr中 (x1,y1) 到 (x2,y2) 位置上的值都+c,就可以通过

对dif[x1][y1]+c实现x1,y1~n,m的子矩阵+c,

然后通过dif[x1][y2+1] - c,dif[x2+1][y1] - c, dif[x2+1][y2+1] +c 实现子矩阵(x1,y1) 到 (x2,y2) 位置上的值都+c。

在代码实现中,为了方便起见,对输入矩阵和差分矩阵的行列大小都进行了+1,把0位置空出来。这样在构造dif矩阵的时候就不需要特判了。

#include <iostream>

using namespace std;

const int MAXLEN = 1e3+10;

int arr[MAXLEN][MAXLEN], dif[MAXLEN][MAXLEN];

int main(){
    int n, m, q; cin >> n >> m >> q;
    for(int i = 1;i <= n; ++i){
        for(int j = 1;j <= m; ++j){
            cin>>arr[i][j];
            dif[i][j] = arr[i][j] - arr[i-1][j] - arr[i][j-1] + arr[i-1][j-1];
        }
    }

    while(q--){
        int x1, y1, x2, y2, c;
        cin>> x1 >> y1 >> x2 >> y2 >> c;
        dif[x1][y1] += c;
        dif[x1][y2+1] -= c;
        dif[x2+1][y1] -= c;
        dif[x2+1][y2+1] += c;
    }

    for(int i = 1;i <= n; ++i){
        for(int j = 1;j <= m; ++j){
            dif[i][j] = dif[i-1][j] + dif[i][j-1] - dif[i-1][j-1] + dif[i][j];
            cout<<dif[i][j]<<' ';
        }
        cout<<endl;
    }
    return 0;
}

#差分的细节

无论是一维还是二维,从差分构造原始数据的时候,要确保前一次的求和能保存下来,存在arr中还是dif中无所谓,根据题意要求就行,比如:

对于一维,可以直接保存在dif中,这样会破坏dif

dif[i] = dif[i-1] + dif[i]

也可以保存在arr中,这样更清晰

arr[i] = arr[i-1] + dif[i]

对于二维,可以直接保存在dif中,这样会破坏dif

dif[i][j] = dif[i-1][j] + dif[i][j-1] - dif[i-1][j-1] + dif[i][j];

也可以保存在arr中,这样更清晰

arr[i][j] = arr[i-1][j] + arr[i][j-1] - arr[i-1][j-1] + dif[i][j]

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值