一、二维前缀和 && 一、二维差分 笔记

转载!写的很好:【学习总结】一、二维前缀和 && 一、二维差分_二维差分求前缀和-CSDN博客

一维前缀

数列的前n项和Sn 减去 数列的前n-1项和Sn-1等于它的通项公式,而将这种方式运用在编程中就能快速算出每一项对应的前缀和。

 
#include <iostream>
using namespace std;
const int N = 1e5+10;
int nums[N] , presum[N] , n , m , l , r; 
int main()
{
    cin>>n>>m;
    for (int i = 1 ; i <= n ; ++i)
    {
        cin>>nums[i];
        presum[i] = presum[i-1] + nums[i];
    }
    while(m--)
    {
        cin>>l>>r;
        cout<<presum[r] - presum[l-1]<<endl; //这就算出来了,多爽啊~
    }
    return 0;
}

二维前缀

一维前缀和s[ i ] = s[ i-1 ] +a[ i ] ; 你会发现对于每一个前缀和都可以利用到它的前一个前缀和 ,然后只需要加上这一项的原数组值,二维前缀和:b[i] [j] = b[i-1][j] + b[i][j-1 ] - b[i-1][ j-1] + a[i] [j]

求子矩阵和

子矩阵和:给你二维数组中两个坐标 (x1 , y1)和 (x2 , y2),以它们作为一个子矩阵的左上角坐标和右下角坐标,求所围成的矩阵中所有元素之和。

Sum(之矩阵和) = b[x2][y2] - b[x2][y1- 1] - b[x1 - 1][y2] + b[x1 - 1][y1 - 1]

 
#include <iostream>
using namespace std;
const int N=1001;
int a[N][N], b[N][N], n, m, q, x1, x2, y1, y2;
int main()
{
    scanf("%d%d%d",&n, &m, &q); 
    for (int i = 1 ; i <= n ; i++){
        for (int j = 1 ; j <= m ; j++){
            scanf("%d", &a[i][j]); 
            b[i][j] = b[i-1][j] + b[i][j-1] - b[i-1][j-1] + a[i][j];//求二维前缀和
        }
    }
    while(q--){
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        printf("%d\n", b[x2][y2] - b[x1 - 1][y2] - b[x2][y1 - 1] + b[x1 - 1][y1 - 1]);
    }
    return 0;
}

一维差分

类似于数学中的求导和积分,差分可以看成前缀和的逆运算

nums[i] = presum[i] - presum[i-1]

 
#include <iostream>
 
using namespace std;
const int N=100010;
int presum[N], nums[N], n, m, l, r, c;
int main(){
    scanf("%d%d", &n, &m);
    for (int i = 1 ; i <= n ; i++){
        scanf("%d", &presum[i]);
        nums[i] = presum[i] - presum[i-1]; //构造一维差分
    }
    while (m--)
    {
        scanf("%d%d%d", &l, &r, &c);
        nums[l] += c; 
        nums[r+1] -= c; //我们只想对[l , r]区间元素产生影响,需要对r ~ n 元素的影响消除掉。 
    }
    for (int i = 1 ; i <= n ; i++){
        presum[i] = presum[i-1] + nums[i];  //我们改变的是nums[i],还要采用一维前缀和求回presum[i]。
        printf("%d ", presum[i]); 
    }
    return 0;
}

二维差分

二维差分就是通过a[][] 的二维前缀和数组b[][] 去求它的二维差分数组a[][]

求二维差分公式:a[i][j] = b[i][j] - b[i-1][j] - b[i][j-1] + b[i-1][j-1];

 
#include <iostream>
using namespace std;
const int N = 1e3 + 10;
int b[N][N], a[N][N];
void insert(int x1, int y1, int x2, int y2, int c)
{
    a[x1][y1] += c;
    a[x2 + 1][y1] -= c;
    a[x1][y2 + 1] -= c;
    a[x2 + 1][y2 + 1] += c;
}
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 >> b[i][j],
            a[i][j] = b[i][j] - b[i][j-1] - b[i-1][j] + b[i-1][j-1];//构建二维差分数组
    while (q--)
    {
        int x1, y1, x2, y2, c;
        cin >> x1 >> y1 >> x2 >> y2 >> c;
        insert(x1, y1, x2, y2, c); //这一步是精髓
    }
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            b[i][j] = b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1] + a[i][j];  //二维前缀和
        }
    }
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            printf("%d ", b[i][j]);//输出操作完所有步骤后的b[][]
        }
        printf("\n");
    }
    return 0;
}

 b数组为空,那么 a数组一开始也为空,但是实际上 b数组并不为空,因此我们每次让 a数组以(i,j)为左上角到以(i,j)为右下角面积内元素(其实就是一个小方格的面积)去插入 c=b[i][j],等价于原数组b中(i,j) 到(i,j)范围内 加上了 b[i][j] (a[][]就是b[][]的二维差分数组呢),因此执行n*m次插入操作,就成功构建了差分a数组

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值