题目链接
题目描述
给定一个长度为 的数组,有
次修改操作,每次操作将指定区间
内的所有元素都加上一个值
。求所有操作完成后的最终数组。
解题思路
本题的核心在于需要对数组进行频繁的区间修改。如果每次修改都遍历 区间,那么
次操作的总时间复杂度将是
,在
和
都很大的情况下会超时。
为了高效地处理区间修改,我们可以引入差分数组。
什么是差分?
差分是前缀和的逆运算。对于一个原数组 ,它的差分数组
定义为:
特别地,我们定义
。
从差分数组 反推原数组
,只需要对
求前缀和即可:
差分如何优化区间修改?
差分数组最强大的特性在于,对原数组 的一个区间
加上一个值
,仅仅需要对差分数组
进行两次单点修改:
增加
:这会导致从
开始的所有元素(
)在计算前缀和时都增加了
。
减少
:为了抵消在
之后的位置上多加的
,我们需要在
处减去
。这会使得从
开始的所有元素(
)在计算前缀和时都减去了
。
这样,两次单点修改的效果叠加起来,就精确地实现了只对区间 加上
的操作。
算法步骤:
- 读入初始数组
。
- 创建一个差分数组
。我们可以创建一个只记录变化量的差分数组,因此初始时全为 0。
- 对于每一次操作
,我们对差分数组进行修改:
并且
。
- 所有
次操作完成后,对差分数组
求前缀和,得到每个位置上的总变化量。
- 将这个总变化量加到原数组
的对应位置上,即可得到最终的数组。
代码
#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()
算法及复杂度
- 算法:差分
- 时间复杂度:
。其中
次操作每次都只修改差分数组的两个点,共
。最后通过一次遍历还原最终数组,需要
。
- 空间复杂度:
,用于存储差分数组。