【题解提供者】吴立强
(差值还原数组)解法
思路
根据定义有: A 1 = B 1 , A i = A i − 1 + B i A_1 = B_1, A_i = A_{i-1}+B_i A1=B1,Ai=Ai−1+Bi。
仔细观察上式可以得到新式: A i = ∑ j = 1 i B j A_i = \sum_{j=1}^i B_j Ai=∑j=1iBj,即每个元素都是一个前缀的和。
代码展示
#include <iostream>
using namespace std;
typedef long long ll;
int main() {
int n; cin >> n;
int sum = 0, x;
while(n --) {
cin >> x;
sum += x;
cout << sum << ' ';
}
return 0;
}
(修改区间中元素)解法
思路
可以直接修改每个指定区间,不过在加强版题目中会 TLE。
注意到,当对一个数组求前缀和时,数组中每个元素值都会加给一个后缀中的所有元素。
例子:
原数组 ---前缀和--> 前缀数组
[0,0,0,0,0] [0,0,0,0,0]
[0,W,0,0,0] [0,W,W,W,W]
[0,0,0,-W,0] [0,0,0,-W,-W]
[0,W,0,-W,0] [0,W,W,0,0]
所以只需要建立一个记录差值的数组,每次区间操作只需要给差值数组修改对应的两个点,其体现在前缀和中就是一个区间的变化。
代码展示
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 100009;
int a[N];
ll c[N]; /// 差值数组
int main() {
int n; cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i];
int m; cin >> m;
while(m --) {
int l, r, w; cin >> l >> r >> w;
c[l] += w, c[r + 1] -= w; /// 等价于给区间 [l,r] 都增加了 w
}
ll sum = 0;
for(int i = 1; i <= n; i ++) {
sum += c[i]; /// 计算前缀和
cout << a[i] + sum << ' '; /// 前缀和即是所有操作对原数组的影响
}
return 0;
}
算法分析
程序时间复杂度为 O ( n + m ) O(n+m) O(n+m),可以通过 1 0 5 10^5 105 的数据。
算法拓展
上述算法为【差分】算法,其可以将区间修改从区间长度优化至两次单点标记(差分标记)和一次前缀和统计。
其实一个完整数组可以转变为差分数组,而转化方式即为【差值还原数组】一题中 A 数组转化为 B 数组的方式,那么代码可以如下写:
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 200009;
ll c[N];
int main() {
int n; cin >> n;
for(int i = 1; i <= n; i ++) cin >> c[i];
for(int i = n; i >= 1; i --) c[i] -= c[i - 1];
int m; cin >> m;
while(m --) {
int l, r, w; cin >> l >> r >> w;
c[l] += w, c[r + 1] -= w;
}
for(int i = 1; i <= n; i ++) {
c[i] += c[i - 1];
cout << c[i] << ' ';
}
return 0;
}