差分(附带例题题解)
什么是差分
差分数组的核心思想是 记录相邻元素的差值,而不是直接存储原始数据。通过维护差分数组,我们可以高效地实现区间增量操作。
1.1 差分数组的定义
给定原数组 a[1..N]
,其差分数组 diff[1..N]
定义为:
diff[1] = a[1]
diff[i] = a[i] - a[i-1]
(i > 1
)
示例:
- 原数组:
a = [1, 3, 5, 7, 9]
- 差分数组:
diff = [1, 2, 2, 2, 2]
1.2 差分数组的作用
差分数组可以快速还原原数组:
a[1] = diff[1]
a[i] = a[i-1] + diff[i]
(i > 1
)
更重要的是,差分数组支持 O(1) 时间的区间增量操作。
2. 区间增量操作
假设我们需要对原数组的区间 [L, R]
中的所有元素加上 val
,即:
a[L] += val
a[L+1] += val
- ...
a[R] += val
如果直接遍历修改,时间复杂度是 O(R - L + 1),最坏情况下是 O(N)。
2.1 差分数组的优化方法
差分数组通过以下方式实现 O(1) 时间的区间增量:
- 在
L
处增加val
:diff[L] += val
- 在
R+1
处减少val
(如果R+1
不越界):diff[R+1] -= val
为什么这样有效?
- 差分数组的前缀和就是原数组,因此:
a[L]
会增加val
(因为diff[L]
增加了val
)。a[L+1..R]
也会增加val
(因为它们依赖于a[L]
的累加)。a[R+1]
不会受到影响(因为diff[R+1]
减少了val
,抵消了前面的增量)。
例题
区间更新
link:1.区间更新 - 蓝桥云课
code
#include <bits/stdc++.h>
using namespace std;int n, m;
int a[100007], diff[100007];void init()
{for(int i = 1; i <= n; i++){diff[i] = a[i] - a[i - 1];}
}void fc(int x, int y, int z)
{diff[x] += z;diff[y + 1] -= z;
}void update()
{for(int i = 1; i <= n; i++){a[i] = diff[i] + a[i - 1];}
}int main()
{while(cin>>n>>m){for(int i = 1; i <= n; i++) cin>>a[i];init();for(int i = 1; i <= m; i++) {int x, y, z; cin>>x>>y>>z;fc(x, y, z);}update();for(int i = 1; i <= n; i++) cout<<a[i]<<" ";cout<<endl;}return 0;
}
小明的彩灯
link:1.小明的彩灯 - 蓝桥云课
code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MAXN = int(5e5 + 7);
int N, Q;
ll a[MAXN], diff[MAXN];int main()
{cin>>N>>Q;for(int i = 1; i <= N; i++) cin>>a[i];for(int i = 1; i <= N; i++) diff[i] = a[i] - a[i - 1];while(Q--){int l, r, x; cin>>l>>r>>x;diff[l] += x;diff[r + 1] -= x;}for(int i = 1; i <= N; i++) a[i] = diff[i] + a[i-1];for(int i = 1; i <= N; i++) cout<<max(0ll, a[i])<<" ";return 0;
}