题目链接

【模板】差分

题目描述

给定一个长度为 的数组,有 次修改操作,每次操作将指定区间 内的所有元素都加上一个值 。求所有操作完成后的最终数组。

解题思路

本题的核心在于需要对数组进行频繁的区间修改。如果每次修改都遍历 区间,那么 次操作的总时间复杂度将是 ,在 都很大的情况下会超时。

为了高效地处理区间修改,我们可以引入差分数组

什么是差分? 差分是前缀和的逆运算。对于一个原数组 ,它的差分数组 定义为: 特别地,我们定义

从差分数组 反推原数组 ,只需要对 求前缀和即可:

差分如何优化区间修改? 差分数组最强大的特性在于,对原数组 的一个区间 加上一个值 ,仅仅需要对差分数组 进行两次单点修改:

  1. 增加 :这会导致从 开始的所有元素()在计算前缀和时都增加了
  2. 减少 :为了抵消在 之后的位置上多加的 ,我们需要在 处减去 。这会使得从 开始的所有元素()在计算前缀和时都减去了

这样,两次单点修改的效果叠加起来,就精确地实现了只对区间 加上 的操作。

算法步骤:

  1. 读入初始数组
  2. 创建一个差分数组 。我们可以创建一个只记录变化量的差分数组,因此初始时全为 0。
  3. 对于每一次操作 ,我们对差分数组进行修改: 并且
  4. 所有 次操作完成后,对差分数组 求前缀和,得到每个位置上的总变化量。
  5. 将这个总变化量加到原数组 的对应位置上,即可得到最终的数组。

代码

#include <iostream>
#include <vector>

using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    int n, q;
    cin >> n >> q;

    vector<long long> a(n);
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
    }

    // 差分数组,b[i] 记录的是 a[i] 相对于 a[i-1] 的增量所发生的变化
    // 多开一个位置防止 r+1 越界
    vector<long long> b(n + 2, 0);

    while (q--) {
        int l, r;
        long long c;
        cin >> l >> r >> c;
        // 数组下标从1开始,需要转换为0-indexed
        b[l - 1] += c;
        b[r] -= c;
    }

    // 通过前缀和计算每个位置的总增量
    for (int i = 1; i < n; ++i) {
        b[i] += b[i - 1];
    }

    // 将增量加到原数组上
    for (int i = 0; i < n; ++i) {
        a[i] += b[i];
    }

    // 输出结果
    for (int i = 0; i < n; ++i) {
        cout << a[i] << (i == n - 1 ? "" : " ");
    }
    cout << "\n";

    return 0;
}
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int q = sc.nextInt();

        long[] a = new long[n];
        for (int i = 0; i < n; i++) {
            a[i] = sc.nextLong();
        }

        // 差分数组,用于记录变化量
        long[] b = new long[n + 1];

        for (int i = 0; i < q; i++) {
            int l = sc.nextInt();
            int r = sc.nextInt();
            long c = sc.nextLong();
            // 题目下标从1开始,转换为0-indexed
            b[l - 1] += c;
            if (r < n) {
                b[r] -= c;
            }
        }

        // 计算每个位置的总增量
        long current_change = 0;
        for (int i = 0; i < n; i++) {
            current_change += b[i];
            a[i] += current_change;
        }

        // 输出结果
        for (int i = 0; i < n; i++) {
            System.out.print(a[i] + (i == n - 1 ? "" : " "));
        }
        System.out.println();
    }
}
import sys

def main():
    n, q = map(int, sys.stdin.readline().split())
    a = list(map(int, sys.stdin.readline().split()))
    
    # 差分数组,用于记录变化量
    b = [0] * (n + 1)
    
    # 处理所有操作,更新差分数组
    for _ in range(q):
        l, r, c = map(int, sys.stdin.readline().split())
        # 题目下标从1开始,转换为0-indexed
        b[l - 1] += c
        if r < n:
            b[r] -= c
            
    # 计算每个位置的总增量并更新原数组
    current_change = 0
    for i in range(n):
        current_change += b[i]
        a[i] += current_change
        
    # 输出结果
    print(*a)

if __name__ == "__main__":
    main()

算法及复杂度

  • 算法:差分
  • 时间复杂度:。其中 次操作每次都只修改差分数组的两个点,共 。最后通过一次遍历还原最终数组,需要
  • 空间复杂度:,用于存储差分数组。