备赛季到了!第十八届蓝桥杯软件赛(C/C++ 程序设计、Python 程序设计)报名 2026-10-08---12-18,省赛定于 2027-04-10;中国电子学会 2026 年 12 月软件编程(C/C++ 一至十级、Python 一至六级)等级考试报名 9/23--12/2,12/12--12/13 统考。这类赛事的算法题里,"给一段区间统一加减"是高频送分又容易翻车的考点------今天用一篇把它讲透。

一、引言:为什么需要差分数组
假设你负责校园里一排路灯的亮度调度。一开始每盏灯有基础亮度,接下来后勤处会反复下达指令:"把第 3 到第 7 盏灯整体调亮 5 度""把第 5 到第 9 盏灯调暗 2 度"......最后你要报出每盏灯的最终亮度,以及全场最亮的灯是哪盏。
最朴素的写法:每条指令都真的去循环修改区间里的每一盏灯。如果灯有 n 盏、指令有 m 条,总时间就是 O(n·m)。当 n、m 都到 10⁵ 量级时,这个做法一定会超时。
差分数组(Difference Array) 就是专门对付"区间整体加/减"的利器:它把每次"区间加"压成两个端点的 O(1) 操作,最后用一次前缀和还原,总时间降到 O(n + m)。这正是蓝桥杯省赛、电子学会 C/C++ 与 Python 考级(三/四级往上)、全国青少年信息素养大赛算法应用赛项里反复出现的套路。
二、题目 / 项目目标
【原创题】校园路灯调光
- 校园里有 n 盏路灯,编号 1 ~ n,初始亮度为
a[1..n](整数,可为 0)。 - 接下来有 m 次调度指令,第 i 次把区间
[l, r]内的每盏灯亮度统一加上v(v 可以是负数,表示调暗)。 - 请计算:
- 每盏路灯的最终亮度;
- 全程亮度最高的那盏灯的亮度值与编号。
数据范围(蓝桥杯风格) :1 ≤ n, m ≤ 10⁵,亮度与增量均为 int 范围内整数,但多次累加后可能超出 int,请用 64 位整数(long long / Python int)。
样例输入
5 4
0 0 0 0 0
1 3 2
2 4 3
3 3 -1
2 5 1
样例输出
2 6 5 4 1
max brightness = 6 at 2
(第 2 盏灯最终最亮,亮度为 6)
三、核心考点
- 差分的定义与性质 :对数组
a,差分数组d[i] = a[i] - a[i-1](约定a[0] = 0)。d的前缀和恰好还原a;反过来a的差分再前缀和还是a------两者互逆。 - 区间加 → 两点改 :对
a的区间[l, r]整体加v,等价于只改d的两个位置:d[l] += v、d[r+1] -= v。其余位置的差分不变,因此单条指令从 O(区间长度) 降到 O(1)。 - 前缀和还原 :处理完所有指令后,
a[i] = a[i-1] + d[i]从前往后扫一遍即可还原最终数组,同时可顺手维护最大值。 - 边界与下标 :
r+1最大会到n+1,数组要开到n+2才安全;下标体系(1-based / 0-based)必须全程统一。 - 带初值的处理 :若初始就有基础亮度
a,别把a直接当差分,而要先把它"差分化"再叠加区间操作(见下方代码)。
四、解法 / 拆解
思路拆解
- 第 0 步:根据初始亮度
a构造差分数组d:d[1] = a[1],d[i] = a[i] - a[i-1](i ≥ 2)。 - 第 1 步(m 次区间加):对每条指令
[l, r, v],执行d[l] += v; d[r+1] -= v。这一步每条 O(1)。 - 第 2 步(前缀和还原 + 求最值):
cur从 0 开始,cur += d[i]即a[i],边还原边记录最大值与位置。 - 复杂度:构造 O(n) + 修改 O(m) + 还原 O(n) = O(n + m);空间 O(n)。
C++ 解法
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
if (!(cin >> n >> m)) return 0;
vector<long long> base(n + 2, 0);
for (int i = 1; i <= n; i++) cin >> base[i];
// 1) 把初始亮度"差分"进 d 数组
vector<long long> d(n + 2, 0);
d[1] = base[1];
for (int i = 2; i <= n; i++) d[i] = base[i] - base[i - 1];
// 2) m 次区间加,每条 O(1)
for (int i = 0; i < m; i++) {
int l, r; long long v;
cin >> l >> r >> v;
d[l] += v;
d[r + 1] -= v; // r+1 最多到 n+1,数组开到 n+2 才不越界
}
// 3) 前缀和还原,同时求最大值
long long cur = 0, mx = -1e18; int mxpos = 0;
for (int i = 1; i <= n; i++) {
cur += d[i];
if (cur > mx) { mx = cur; mxpos = i; }
cout << cur << (i == n ? "" : " ");
}
cout << "\nmax brightness = " << mx << " at " << mxpos << "\n";
return 0;
}
Python 解法
python
import sys
def solve():
data = sys.stdin.read().split()
if not data:
return
it = iter(data)
n = int(next(it)); m = int(next(it))
base = [0] + [int(next(it)) for _ in range(n)]
# 1) 把初始亮度差分进 d 数组
d = [0] * (n + 2)
d[1] = base[1]
for i in range(2, n + 1):
d[i] = base[i] - base[i - 1]
# 2) m 次区间加,每条 O(1)
for _ in range(m):
l = int(next(it)); r = int(next(it)); v = int(next(it))
d[l] += v
d[r + 1] -= v
# 3) 前缀和还原,同时求最大值
cur = 0; mx = -10**18; mxpos = 0
out = []
for i in range(1, n + 1):
cur += d[i]
if cur > mx:
mx = cur; mxpos = i
out.append(str(cur))
print(" ".join(out))
print(f"max brightness = {mx} at {mxpos}")
if __name__ == "__main__":
solve()
两个版本对样例均输出 2 6 5 4 1 与 max brightness = 6 at 2,与手算一致。
五、进阶
1. 二维差分(矩阵子矩形整体加)
把一行扩展成网格:想把以 (x1, y1) 为左上、(x2, y2) 为右下的矩形整体加 v,只需四个角做 O(1) 标记:
d[x1][y1] += v
d[x1][y2+1] -= v
d[x2+1][y1] -= v
d[x2+1][y2+1] += v
最后做二维前缀和还原。这是蓝桥杯"填空/涂色/矩阵操作"题的常客。
2. 区间加 + 区间求和(二阶差分)
如果还要频繁查询"某段区间的和",一阶差分不够用了。思路是对差分数组再差分一次(二阶差分),或改用树状数组 / 线段树做区间加、区间求和。理解一阶差分是啃下这些数据结构的前置功。
3. 差分数组 = 离线一维扫描线
"区间加 1 / 减 1"的本质是扫描线:把所有区间左端点 +v、右端点之后 -v,从左到右扫一遍前缀和,就得到每个位置被覆盖的次数。经典应用:会议室冲突数、天数重叠统计、至少被 k 个区间覆盖的最大长度。
4. 与"前缀和"对照记忆(别混)
-
前缀和 :擅长"区间求和转单点查"(用
S[r] - S[l-1])。 -
差分 :擅长"区间加转变点改"(用
d[l]+=v, d[r+1]-=v)。
两者是一对互逆操作,经常在同一道题里配合使用(例如先差分做区间加,再前缀和还原后做区间求和)。
六、小结与互动
差分数组是"用空间换时间、用端点代替区间"思想的典型代表:把每次 O(区间长度) 的修改压缩成 O(1) 的两个端点操作,最后用一次 O(n) 前缀和收尾。它常出现在蓝桥杯省赛填空与编程大题、电子学会 C/C++ 与 Python 考级(三/四级往上)、全国青少年信息素养大赛算法应用赛项中,属于"性价比极高"的基础必会算法。
易错点速记(考前必看)
-
r+1越界:数组至少开到n+2,否则最后一个区间会写爆边界。 -
下标不统一:区间用 1-based 但数组 0-based,整体错一位。
-
忘记前缀和还原,直接把
d当答案输出。 -
带初值时差分初值算错:应是
d[1]=a[1]、d[i]=a[i]-a[i-1],不是d[i]=a[i]。 -
把"区间加"误用前缀和、把"区间求和"误用差分------二者方向正好相反。
-
多组数据没把
d和cur/mx彻底重置。 -
累加溢出:用
long long(C++)或放心用 Pythonint,别用 32 位int硬扛。
练一练:如果把题目改成"求最终数组里等于某个值 k 的元素个数",或"求被至少 2 条指令覆盖过的灯的数量",你会怎么用差分快速得到?欢迎在评论区贴出你的思路或代码,我们一起交流避坑!
觉得有用就点个赞 + 收藏,备赛季不迷路;关注我,持续更新蓝桥杯 / 电子学会考级 / 信息素养大赛算法干货。
📚 免费少儿编程资料(夸克网盘领取)
以下资料来自夸克网盘分享,点击链接可直接保存;若需在 App 内打开,也可复制下方明文链接:
- 全国青少年信息素养大赛复赛集训题目Python&C++.docx
https://pan.quark.cn/s/93995d3cb150 - 2024信息素养-智能算法应用挑战赛-复赛初中组题目7月7日.pdf
https://pan.quark.cn/s/da97b5dbf75d - Python背记手册.pdf
https://pan.quark.cn/s/7568ae9ca92b - Python课程
https://pan.quark.cn/s/a94bf02d00c6 - 2024信息素养大赛图形化复赛集训题答案3-9
https://pan.quark.cn/s/6ccab7ec3cbc - 2025年03月份电子学会考级真题
https://pan.quark.cn/s/4403c4228912 - 2025全国青少年信息素养大赛赛项说明
https://pan.quark.cn/s/d9d0df4a9f29 - 青少儿信息素养大赛编程资料
https://pan.quark.cn/s/4ab6bd83be8a
资料持续更新,关注获取最新分享。