前缀和与差分CSP-J

目录

💡 核心思想

前缀和是一种预处理技巧,用 $O(n)$ 预处理后可以在 $O(1)$ 时间内查询任意区间的元素之和。差分是前缀和的逆运算,用于 $O(1)$ 区间修改、$O(n)$ 查询最终结果。

前缀和差分是一对互逆操作,理解了其中一个另一个自然就懂了。前缀和的核心思想是"用空间换时间"——先花 $O(n)$ 算好前缀和数组,之后每次区间查询就是 $O(1)$。差分则反过来:先在差分数组上 $O(1)$ 做区间加减,最后 $O(n)$ 前缀和还原。

🎯 直觉理解

前缀和就像记账:你记下"到第 i 天为止总共花了多少钱",那"第3天到第7天花了多少"就是"到第7天的总额 - 到第2天的总额"。差分就像"我今天比昨天多花了10块"——知道每天的差值就能还原每天的总额。

📝 算法流程

  1. 一维前缀和:$s[i] = s[i-1] + a[i]$,区间和 $[l,r] = s[r] - s[l-1]$
  2. 一维差分:$d[l]+=v, d[r+1]-=v$,最后求前缀和还原
  3. 二维前缀和:容斥原理
  4. 二维差分:四个角操作

$$\sum_{i=l}^{r} a[i] = s[r] - s[l-1]$$

$$\text{差分:} d[l] \mathrel{+}= v,\ d[r+1] \mathrel{-}= v$$

📊 复杂度分析

指标复杂度
时间预处理 $O(n)$,查询 $O(1)$
空间$O(n)$

💻 参考实现(C++)

C++ (C++17)
#include <bits/stdc++.h>
using namespace std;
int main() {
    int n, q; cin >> n >> q;
    vector<long long> a(n+2, 0), s(n+1, 0);
    for (int i = 1; i <= n; i++) { cin >> a[i]; s[i] = s[i-1] + a[i]; }
    while (q--) {
        int l, r; cin >> l >> r;
        cout << s[r] - s[l-1] << "\n";
    }
    // 差分数组示例
    vector<long long> d(n+2, 0);
    // 区间 [l, r] 加 v
    auto add = [&](int l, int r, long long v) { d[l] += v; d[r+1] -= v; };
    // 还原:对 d 求前缀和
    return 0;
}

⚠️ 常见坑点

前缀和数组下标从 1 开始,s[0]=0 避免特判

差分数组大小要开 n+2 防止 r+1 越界

二维前缀和容斥公式:$s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1]$

long long 防止溢出

📚 相关题目

题目来源难度备注
P1115 最大子段和洛谷CSP-J前缀和思想
P1083 借教室洛谷CSP-S差分+二分