差分数组是一种用于高效处理区间更新和区间查询的数据结构,特别称为差分序列。它的核心思想是通过记录数组元素之间的差值,将多次次区间更新操作优化为常数时间操作,非常适合处理频繁的区间增减问题。

差分数组的基本原理

假设有一个原始数组 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,使用差分数组只需两步:

  1. diff[l] += val
  2. diff[r+1] -= val(如果 r+1 不超过数组范围)

这种操作的时间复杂度是 O(1),而直接操作原始数组需要 O(r-l+1) 的时间。

差分数组的应用步骤

  1. 初始化差分数组(全为0)
  2. 对所有区间更新操作,在差分数组上做标记
  3. 计算差分数组的前缀和,得到更新后的原始数组

代码示例:差分数组的实现与应用

#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;
}

差分数组的典型应用场景

  1. 区间增减操作:如频繁地对数组的某些区间进行加减操作
  2. 统计覆盖次数:如计算多个区间重叠部分的覆盖次数
  3. 前缀和相关问题:如本文开头的答题闯关问题,需要统计满足条件的数量

与前缀和的关系

  • 差分数组是前缀和数组的逆运算
  • 前缀和数组用于快速计算区间和,差分数组用于快速进行区间更新
  • 两者结合可以高效处理复杂的数组操作问题

通过差分数组,我们可以将原本需要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 被多少个研究生的区间所覆盖。
寻找最大值:在计算前缀和的过程中,我们跟踪最大值,这个值就是最多能获得认证的研究生数量。