差分数组是一种用于高效处理区间更新和区间查询的数据结构,特别称为差分序列。它的核心思想是通过记录数组元素之间的差值,将多次次区间更新操作优化为常数时间操作,非常适合处理频繁的区间增减问题。
差分数组的基本原理
假设有一个原始数组 arr,我们定义其差分数组 diff 如下:
diff[0] = arr[0]diff[i] = arr[i] - arr[i-1](对于i > 0)
通过差分数组可以快速还原出原始数组:
arr[i] = diff[0] + diff[1] + ... + diff[i](即差分数组的前缀和)
核心优势:区间更新的高效性
如果要对原始数组的区间 [l, r] 中的所有元素都加上一个值 val,使用差分数组只需两步:
diff[l] += valdiff[r+1] -= val(如果r+1不超过数组范围)
这种操作的时间复杂度是 O(1),而直接操作原始数组需要 O(r-l+1) 的时间。
差分数组的应用步骤
- 初始化差分数组(全为0)
- 对所有区间更新操作,在差分数组上做标记
- 计算差分数组的前缀和,得到更新后的原始数组
代码示例:差分数组的实现与应用
#include <iostream>
#include <vector>
using namespace std;
// 使用差分数组对区间[l, r]加val
void range_add(vector<int>& diff, int l, int r, int val) {
diff[l] += val;
if (r + 1 < diff.size()) {
diff[r + 1] -= val;
}
}
// 从差分数组还原原始数组
vector<int> restore_array(const vector<int>& diff) {
vector<int> arr(diff.size());
arr[0] = diff[0];
for (int i = 1; i < diff.size(); ++i) {
arr[i] = arr[i - 1] + diff[i];
}
return arr;
}
int main() {
int n = 5; // 原始数组大小
vector<int> diff(n + 2, 0); // 差分数组,多开2个位置防止越界
// 示例:对区间[1,3]加2,对区间[2,4]加3
range_add(diff, 1, 3, 2);
range_add(diff, 2, 4, 3);
// 还原数组
vector<int> arr = restore_array(diff);
// 输出结果
for (int i = 1; i <= n; ++i) {
cout << arr[i] << " "; // 输出:0 2 5 5 3
}
return 0;
}
差分数组的典型应用场景
- 区间增减操作:如频繁地对数组的某些区间进行加减操作
- 统计覆盖次数:如计算多个区间重叠部分的覆盖次数
- 前缀和相关问题:如本文开头的答题闯关问题,需要统计满足条件的数量
与前缀和的关系
- 差分数组是前缀和数组的逆运算
- 前缀和数组用于快速计算区间和,差分数组用于快速进行区间更新
- 两者结合可以高效处理复杂的数组操作问题
通过差分数组,我们可以将原本需要O(n)时间的区间更新操作优化到O(1),大大提高了程序效率,特别适合处理大规模数据。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
// 初始化差分数组,大小为m+2以避免越界
vector<int> diff(m + 2, 0);
for (int i = 0; i < n; ++i) {
int L, R;
cin >> L >> R;
// 对区间[L, R]进行+1操作
diff[L]++;
if (R + 1 <= m) {
diff[R + 1]--;
}
}
// 计算前缀和,寻找最大值
int max_count = 0;
int current = 0;
for (int p = 1; p <= m; ++p) {
current += diff[p];
max_count = max(max_count, current);
}
cout << max_count << endl;
return 0;
}
代码解析
差分数组初始化:我们创建了一个大小为 m+2 的差分数组,额外的空间是为了避免处理 R+1 时的越界问题。
区间更新:对于每个研究生的需求区间 [L, R],我们在差分数组上执行:
diff [L]++:表示从 L 开始,覆盖计数增加 1
diff [R+1]--:表示从 R+1 开始,覆盖计数减少 1
计算前缀和:通过遍历差分数组并累加,我们得到每个资料 p 被多少个研究生的区间所覆盖。
寻找最大值:在计算前缀和的过程中,我们跟踪最大值,这个值就是最多能获得认证的研究生数量。
浙公网安备 33010602011771号