zoukankan      html  css  js  c++  java
  • 前缀和(一维与二维) 差分

    一维前缀和

    很简单,可以联想等差数列求每一段和的公式 Sij = Sj - Si

    一维的前缀和的初始化就是 S[i] = S[i-1] + a[i] 前缀和数组从1开始初始化

    所以想要求 x -> y 段和 公式就是 res = S[y] - S[x-1]

    代码

    #include<iostream>
    #include<cstdio>
    using namespace std;
    
    const int N = 100010;
    int a[N], b[N];
    
    int main()
    {
        int m, n;
    
        scanf("%d%d", &m, &n);
        for(int i = 1; i <= m; i++) scanf("%d", &a[i]);
        
        for(int i = 1; i <= m; i++) b[i] = b[i-1]+a[i];
        
        while(n--)
        {
            int l, r;
            scanf("%d%d", &l, &r);
            printf("%d
    ", b[r] - b[l-1]);
        }
    
        system("pause");
        return 0;
    }
    

    二维前缀和

    典型例题就是求矩阵中子矩阵的和

    S[i][j] 就是从矩阵左上角到右下角i j的子矩阵和

    根据几何:看图

    初始化的公式就是: s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j];

    求子矩阵公式: res = s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1]

    这两个公式可以互推

    代码:

    #include <iostream>
    
    using namespace std;
    
    const int N = 1010;
    int n, m, q;
    int a[N][N], s[N][N];
    
    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;
            int res = s[x2][y2] - s[x2][y1-1] - s[x1-1][y2] + s[x1-1][y1-1];
            cout << res << endl;
        }
        system("pause");
        return 0;
    }
    

    关于坐标为什么要减一

    这里的意思是我们直接把小格作为坐标而不是交点,看图,求图中绿色子矩阵的和

    一维差分

    差分就是求前缀和的逆运算,假设对数组b是a的差分数组 那么a就是b的前缀和数组

    作用:主要作用就是想要在a数组里给定 [l, r] 区间里各加一个数,我们可以用一个循环,用O(n)完成 但是有了差分后可以用O(1)完成这个工作

    假设b是a的差分数组 那么 b[i]+c ,那么 a[i]~a[n] 全部都会加c,因为a相当于b的前缀和数组 所以利用这个性质 我们只需要操作两个数就可以达到这个作用

    b[l] += c ;

    b[r] -= c ;

    看图:

    对于b数组的构造我们不用去管 我们可以假设一开始a b 两个数组都为0,那么输入(a[i])的过程我们就可以认为是对b做了n次插入操作,第一次就是([1, 1] + a[1]), 第二次([2, 2] + a[2]), ....那么每往a中插一个数字 就相当于往b中差一个数字 所以我们只需遍历一遍a 插入到b中即可 所以我们的 insert 可以通用

    最后再对b求一遍前缀和 就是我们的答案

    代码:

    #include <iostream>
    #include <cstdio>
    using namespace std;
    
    const int N = 100010;
    int n, m;
    int a[N], b[N];
    
    void insert(int l, int r, int c)
    {
        b[l] += c;
        b[r+1] -= c;
    }
    
    //总体思路就是先差分 之后再求前缀和
    int main()
    {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
    
        for(int i = 1; i <= n; i++) insert(i, i, a[i]);  //第一步就是假设两个a b数组都为0,然后可以认为输入的每个数据都是现插入的   
                                                         //这样就可以直接构造原来数组的差分数组
        while(m--)
        {
            int l, r, c;
            scanf("%d%d%d", &l, &r, &c);
            insert(l, r, c);
        }
    
        for(int i = 1; i <= n; i++) b[i] += b[i-1];
     
        for(int i = 1; i <= n; i++) printf("%d ", b[i]);
    
        system("pause");
        return 0;
    }
    

    二维差分

    构造:类比于一维

    核心代码:

        b[x1][y1] += c;
        b[x2+1][y1] -= c;
        b[x1][y2+1] -= c;
        b[x2+1][y2+1] += c;
    

    看图:

    代码:

    #include <iostream>
    #include <cstdio>
    using namespace std;
    
    const int N = 1010;
    int n, m, q;
    int a[N][N], b[N][N];
    
    void insert(int x1, int y1, int x2, int y2, int c)
    {
        b[x1][y1] += c;
        b[x2+1][y1] -= c;
        b[x1][y2+1] -= c;
        b[x2+1][y2+1] += c;
    }
    
    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]);
    
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                insert(i, j, i, j, a[i][j]);
    
        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];
    
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j<= m; j++)
            {
                cout << b[i][j] << " ";
            }
            cout << endl;
        }
        system("pause");
        return 0;
    }
    
  • 相关阅读:
    添加unique约束
    设置自增列的步长和起始值
    创建外键及主键
    绘制折线图和叠加区域图
    绘制饼图
    生成叠加柱状图和水平柱状图
    随机生成带有颜色的验证码
    os模块与操作系统的交互
    TDate赋值给Variant时注意的问题
    线程中的异常处理
  • 原文地址:https://www.cnblogs.com/ZhengLijie/p/13399693.html
Copyright © 2011-2022 走看看