zoukankan      html  css  js  c++  java
  • 前缀与差分

    前缀与差分

    1. 算法分析

    1.1 前缀和

    定义
    s[n] = (sum_{i=1}^na[i])

    递推关系
    s[i] = a[i] + s[i - 1]

    区间求和
    (sum_{i=l}^ra[i] = s[r] - s[l - 1])

    1.2 差分

    定义
    存在两个数组a(a1, a2, a3,..., an)和b(b1, b2, ... ,bn)
    如果ai = b1 + b2 + ... + bi
    那么b称为a的差分(比如: b1 = a1, b2 = a2 - a1)

    作用

    1. 区间增加->单点修改:当a[l]~a[r]这个区间内元素全加上c时,只需要对b[l] + c, b[r+1] - c即可,因为:b[l] + c:让a[l]往后的元素全部加上c。b[r+1]-c:防止a[r+1]开始往后的元素加上c
    2. 一开始给ai赋值时,可以看成是给a[i]~a[i]这段全部加上c

    构造
    差分的构造可以选择: b[i] = a[i] - a[i - 1]

    变形
    差分可以维护a的变化值,即b[i] = Δai = $ sum_{i=1}^nb[i] $

    2. 板子

    2.1 前缀和

    2.1.1 一维前缀和

    #include <bits/stdc++.h>
    
    using namespace std;
    
    int const N = 1e5 + 10;
    int s[N], a[N];
    
    int main()
    {
        int n, k;
        cin >> n >> k;
        // 计算前缀和
        for (int i = 1; i <= n ; ++i)
        {
            scanf("%d", &a[i]);
            s[i] = s[i - 1] + a[i];
        }
        
        // 使用前缀和
        for (int i = 0; i < k ; ++i)
        {
            int l, r;
            scanf("%d %d", &l, &r);
            printf("%d
    ", s[r] - s[l -1]);
        }
        return 0;
    }
    

    2.1.2 二维前缀和

    #include <bits/stdc++.h>
    
    using namespace std;
    
    int const N = 1e3 + 10;
    int s[N][N], a[N][N];
    
    int main()
    {
        int n, m, k;
        cin >> n >> m >> k;
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j)
            {
                scanf("%d", &a[i][j]);
                s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];  // 前缀和在二维的情况
            }
        for (int i = 0; i < k; ++i)
        {
            int l1, r1, l2, r2;
            scanf("%d %d %d %d", &l1 ,&r1, &l2, &r2);
            printf("%d
    ", s[l2][r2] - s[l2][r1 - 1] - s[l1 - 1][r2] + s[l1 - 1][r1 - 1]);  // 打印结果
        }
        return 0;
    }
    

    2.2 差分

    2.2.1 一维差分

    #include <bits/stdc++.h>
    
    using namespace std;
    
    int const N = 1e5 + 10;
    int b[N], n, m, a[N];
    
    // 区间修改
    void insert(int l, int r, int c) {
        b[l] += c, b[r + 1] -= c;
    }
    
    int main() {
        cin >> n >> m;
        for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);  // 读入原数组
        for (int i = 1, l, r, c; i <= m; ++i) {  // 不断读入区间增加
            scanf("%d %d %d", &l, &r, &c);
            insert(l, r, c);  
        }
        for (int i = 1; i <= n; ++i) {
            b[i] += b[i - 1];  // 计算a[i]的变化值
            printf("%d ", a[i] + b[i]);
        }
        return 0;
    }
    

    2.2.1 二维差分

    #include <bits/stdc++.h>
    
    using namespace std;
    
    int const N = 1e3 + 10;
    int a[N][N], b[N][N];
    int n, m, q;
    
    // 区间增加(二维情况)--和前缀和情况不一样
    void insert(int l1, int r1, int l2, int r2, int c)
    {
        b[l1][r1] += c;
        b[l2 + 1][r2 + 1] += c;
        b[l2 + 1][r1] -= c;
        b[l1][r2 + 1] -= c;
    }
    
    int main() {
        cin >> 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, l1, r1, l2, r2, c; i <= q; ++i) {
            scanf("%d%d%d%d%d", &l1, &r1, &l2, &r2, &c);  // 区间增加
            insert(l1, r1, l2, r2, 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];  // 前缀求和,计算变化量
                printf("%d ", a[i][j] + b[i][j]);  // 计算变化后的a[i][j]
            }
            printf("
    ");
        }
        return 0;
    }
    
  • 相关阅读:
    30 分钟快速入门 Docker 教程
    python functools.wraps
    计算机科学中最重要的32个算法
    JDBC的作用及重要接口
    SSO单点登录--支持C-S和B-S
    谈谈Sql server 的1433端口
    屏蔽:粘贴到KindEditor里,IE下弹出框报”对象不支持moveToElementText属性或方法“错误的提示
    markdown
    ddd
    python进阶学习(一)--多线程编程
  • 原文地址:https://www.cnblogs.com/spciay/p/13064104.html
Copyright © 2011-2022 走看看