登录以参加训练计划
“基础算法串讲:桶、前缀和与差分、二分、贪心”,是 CSP-J(普及组)最核心、最必考的四大基石。如果说数据结构是内功,那这四个就是你行走江湖最常用的四套剑法。
下面我为你详细拆解每一个知识点的核心逻辑、应用场景以及考场上的“坑”。
一、 桶 (Bucket) / 计数排序
1. 核心思想
“空间换时间”。
不把数字 当作数值,而是把它当作数组的下标。
比如有一个数组 cnt,cnt[x] 表示数字 出现了多少次,或者 是否存在。
2. 适用场景
- 数值范围小:数据的数值(Value)不大(比如 ),哪怕数据量()很大也没关系。
- 去重/排序:需要快速排序且数值密集,或者需要快速判断“有没有”。
- 找众数:统计谁出现次数最多。
3. 代码模板
int bucket[1000005]; // 注意内存限制,通常不超过 10^7
// 统计
for(int i=1; i<=n; i++) {
cin >> x;
bucket[x]++;
}
// 遍历(天然有序)
for(int i=0; i<=max_val; i++) {
while(bucket[i] > 0) {
cout << i << " ";
bucket[i]--;
}
}
4. 教练提示
- 坑点:如果题目说数字可能达到 ,或者有负数,普通的数组桶就失效了。这时需要用
std::map或者先进行“离散化”。
二、 前缀和与差分 (Prefix Sum & Difference)
这两个是一对互逆运算,专门用来解决区间问题,能把 的操作降维到 。
1. 前缀和 (Prefix Sum)
- 定义:。
- 递推公式:
S[i] = S[i-1] + a[i]。 - 必杀技: 求静态区间和。
- 求 到 的和?
- 答案直接是:
S[R] - S[L-1]。
- 进阶:二维前缀和(容斥原理)。
2. 差分 (Difference)
- 定义:。
- 性质:差分数组的前缀和 = 原数组。
- 必杀技: 进行区间修改。
- 任务:把区间 内所有数都加上 。
- 暴力做法要 ,差分做法只要两步:
D[L] += vD[R+1] -= v(注意是 R+1)
- 流程:构建差分 修改差分 求前缀和还原数组。
3. 教练提示
- 下标:强烈建议数组下标从 1 开始,这样
S[L-1]当 时就是S[0]=0,不用特判边界。
三、 二分 (Binary Search)
将 的查找优化为 。前提是答案具有单调性(有序)。
1. 二分查找 (Binary Search on Array)
在一个有序数组里找某个数,或者找第一个 的数。
- STL 神器:
lower_bound(找 ) 和upper_bound(找 )。 - 手写:
mid = (l + r) >> 1。
2. 二分答案 (Binary Search on Answer) —— 重点中的重点
题目让你求“最大值最小”或“最小值最大”时,90% 是二分答案。
- 思路:
- 不去直接算答案,而是猜一个答案 。
- 写一个
check(mid)函数:如果答案是 ,能满足题目要求吗? - 如果满足,就尝试更好的答案;如果不满足,就换个方向猜。
- 模板(查找左边界):
int l = 1, r = 1e9, ans = -1; while (l <= r) { int mid = l + (r - l) / 2; if (check(mid)) { ans = mid; // 记录答案 r = mid - 1; // 往左找更小的 } else { l = mid + 1; } }
3. 教练提示
- 死循环:手写二分最容易写成死循环(
l=mid还是mid+1?)。建议固定背诵一种模板(如l <= r配合mid+1/mid-1),或者直接用记录ans的方法。
四、 贪心 (Greedy)
1. 核心思想 “目光短浅”。在每一步选择中,都采取在当前状态下最好/最优的选择(局部最优),从而希望导致结果是全局最优。
2. 常见套路
- 区间问题:比如“最多能参加多少个活动”。通常按右端点排序。
- 排序不等式:比如“排队打水问题”,让快的人先打,总等待时间最短。
- 合并果子:每次挑最小的两堆合并(配合优先队列)。
- 字典序:为了让字符串字典序最小,通常把小的字符尽可能放在前面。
3. 教练提示
- 玄学:贪心是最容易想的,也是最容易错的。
- 验证:在考场上,如果你想到了一个贪心策略,最好先手动模拟几个特殊的样例(比如极端的、相等的、包含的),看看对不对。
- 结合:贪心经常和二分结合(在
check函数里贪心)。
总结
这四个知识点在 CSP-J/S 里的地位:
- 桶:基础工具,用来优化统计。
- 前缀和/差分:必考的预处理技巧,看到“区间查询”或“区间修改”就要由条件反射。
- 二分:解决“最大值最小”类问题的通解。
- 贪心:思维题的核心,决定你能否快速切掉前两题。
把这四块弄通透,普及组一等奖就稳了一大半!加油!
你好!我是你的 OI 教练。
这是一个非常好的实战请求。理解了理论(桶、前缀和等)之后,必须通过刷题来固化肌肉记忆。
“桶”、“Map”和“离散化” 其实是处理数据映射的三种不同境界,对应了不同的数据规模。
我为你精选了洛谷上具有代表性的题目,按难度和考点分类整理如下:
一、 数组桶 (Bucket) / 计数排序
特点:数值小(通常 ),速度最快 ,但吃内存。
| 题目编号 | 题目名称 | 难度 | 训练重点 |
|---|---|---|---|
| P1271 | [深基9.例1] 选举学生会 | 🔴 入门 | 裸题。数值极小,直接开桶统计并输出,最基础的计数排序。 |
| P1047 | [NOIP2005 普及] 校门外的树 | 🟠 普及- | 必刷。用布尔数组桶 vis[x] 标记某个位置是否有树,经典的覆盖问题。 |
| P1059 | [NOIP2006 普及] 明明的随机数 | 去重+排序。利用桶的天然有序性和唯一性,一次遍历解决去重和排序。 | |
| P5744 | [深基7.习9] 培训 | 🔴 入门 | 虽然是结构体,但结合简单的桶思想可以统计分数段等信息。 |
| P1428 | 小鱼比可爱 | 虽然是暴力题,但可以用桶思想理解(统计比自己小的)。 |
二、 STL std::map (映射)
特点:数值大(甚至可以是字符串),比较稀疏,速度稍慢 ,写法最简单。
| 题目编号 | 题目名称 | 难度 | 训练重点 |
|---|---|---|---|
| P1102 | A-B 数对 | 🟡 普及/提高- | 经典。暴力 会超时,用 map 统计每个数出现的次数,将查找变为 或 。 |
| P5266 | [深基17.例6] 学籍管理 | 模板题。纯粹考察 map 的增删改查操作,Key 是字符串(姓名)。 |
|
| P3879 | [TJOI2010] 阅读理解 | 一对多映射。单词对应多篇文章,考察 map<string, vector<int>> 的用法。 |
|
| P1918 | 保龄球 | 🟠 普及- | 给定数值找下标。由于数值很大,无法开数组,必须用 map 记录 mp[val] = index。 |
| P3370 | 【模板】字符串哈希 | 🟡 普及/提高- | 虽然本意是考 Hash 算法,但用 std::map 或 std::set 也能通过(卡常除外),适合练习去重。 |
三、 离散化 (Discretization)
特点:数值极大()但数据个数少(),只关心相对大小,不关心绝对数值。通常配合线段树、树状数组或并查集使用。
核心代码模板(必背):
sort(a + 1, a + n + 1);
int len = unique(a + 1, a + n + 1) - (a + 1);
// 查询 x 离散化后的值:
int id = lower_bound(a + 1, a + len + 1, x) - a;
| 题目编号 | 题目名称 | 难度 | 训练重点 |
|---|---|---|---|
| P1496 | 火烧赤壁 | 🟡 普及/提高- | 基础。坐标达到 ,但只有几千个区间。需要把坐标离散化后再计算覆盖长度。 |
| P1955 | [NOI2015] 程序自动分析 | 🔵 提高+/省选- | 经典综合。并查集题目,但数字高达 ,必须先离散化才能放入并查集的数组中。 |
| P1908 | 逆序对 | 🟡 普及/提高- | 求逆序对。如果用树状数组做,数值很大时必须离散化(当然归并排序做法不需要)。 |
| P3029 | [USACO11NOV] Cow Lineup S | 双指针+离散化。奶牛的 ID 是大整数,需要映射成小的 ID 才能开数组统计频率。 | |
| P8218 | [深基10.例6] 求区间和 | 🟠 普及- | 虽然这题本身不需要,但你可以试着假设 但数值很大,强行用离散化+前缀和来练习。 |
🏆 教练的总结建议
这三种技术其实是解决同一个问题(如何通过数值找位置)的三个阶段:
-
能开数组桶,就开数组桶:
- 题目说 既然能用
int a[1000005],就别搞复杂的。 - 例题:P1271
- 题目说 既然能用
-
数值很大,或者是字符串,且不需要维护复杂的区间关系:
- 用
map。虽然慢一点(带 log),但代码只需要两行,考场上性价比最高。 - 例题:P1102, P5266
- 用
-
数值很大,且需要作为数组下标去构建高级数据结构(如树状数组、并查集):
- 必须用 离散化。
map的 log 可能会导致总时间超时(TLE),或者map无法支持树状数组的连续性要求。 - 例题:P1955, P1908
- 必须用 离散化。
建议按照 数组桶 Map 离散化 的顺序刷题,循序渐进!加油!
前缀与差分洛谷题单:
前缀和 (Prefix Sum) 与 差分 (Difference) 是 OI 中性价比最高的算法之一。它们的特点是:代码短,数学味重,能把复杂度从 降到 。
这对算法就像“倚天剑”和“屠龙刀”:
- 前缀和:专攻 “静态区间查询”。
- 差分:专攻 “区间修改”。
下面我为你整理了一份循序渐进的洛谷刷题单,从板子题到经典应用题,按难度梯队排列。
第一阶段:一维基础(板子题)
目标:背诵并熟练掌握一维的公式,坚决不写 的暴力。
- 前缀和公式:
sum[i] = sum[i-1] + a[i];区间[L, R]和为sum[R] - sum[L-1]。 - 差分公式:
b[L] += v; b[R+1] -= v;最后做一次前缀和还原。
| 题目编号 | 题目名称 | 难度 | 训练重点 |
|---|---|---|---|
| P8218 | [深基10.例6] 求区间和 | 🟠 普及- | 【纯板子】 这就是前缀和定义的直接应用。一定要注意下标从 1 开始,防止越界。 |
| P2367 | 语文成绩 | 【纯板子】 这就是差分定义的直接应用。全班修改成绩,最后一次性输出。 | |
| P1115 | 最大子段和 | 经典贪心/DP。但也可以用前缀和理解:sum[i] - min(sum[0...i-1]) 就是以 i 结尾的最大子段和。 |
|
| P5638 | 【CSGRound2】光骓者的荣耀 | 应用。求某段区间的和(前缀和)+ 简单的贪心选择。 |
第二阶段:二维进阶(矩阵操作)
目标:掌握二维空间下的公式,这是一个难点,容易写错坐标。
- 二维前缀和(容斥原理):
S[i][j] = S[i-1][j] + S[i][j-1] - S[i-1][j-1] + a[i][j] - 子矩阵和:
S[x2][y2] - S[x1-1][y2] - S[x2][y1-1] + S[x1-1][y1-1] - 二维差分:
D[x1][y1]+=c; D[x1][y2+1]-=c; D[x2+1][y1]-=c; D[x2+1][y2+1]+=c
| 题目编号 | 题目名称 | 难度 | 训练重点 |
|---|---|---|---|
| P1719 | 最大加权矩形 | 🟡 普及/提高- | 二维前缀和。需要枚举矩阵的左上角和右下角(或者压缩行),利用二维前缀和 算矩阵内的值。 |
| P2004 | 领地选择 | 必做。在一个大地图里找一个 的正方形,使其和最大。标准的二维前缀和应用。 | |
| P3397 | 地毯 | 🟠 普及- | 二维差分。铺地毯问题。这题既可以用“一维差分(对每一行做)”,也可以用“二维差分”直接做。 |
第三阶段:巧妙应用(数学与思维)
目标:题目没有明说“求区间和”,但你需要把问题转化为前缀和/差分模型。
| 题目编号 | 题目名称 | 难度 | 训练重点 |
|---|---|---|---|
| P3406 | 海底高铁 | 🟡 普及/提高- | 差分应用。看起来是图论或模拟,其实是统计每段铁路被经过了多少次。用差分数组标记覆盖次数。 |
| P3131 | [USACO16JAN] Subsequences... | 同余前缀和。求区间和能被 7 整除。利用性质:若 (S[i] - S[j]) % 7 == 0,则 S[i] % 7 == S[j] % 7。结合 桶/Map 来记录余数位置。 |
|
| P1314 | [NOIP2011 提高] 聪明的质监员 | 🔵 提高+/省选- | 二分答案+前缀和。非常经典的综合题。需要二分 ,check 过程中用前缀和快速计算检验值。 |
第四阶段:压轴神题(差分+二分)
目标:掌握 CSP-S / NOIP 级别的常见套路,即“二分答案,差分验证”。
| 题目编号 | 题目名称 | 难度 | 训练重点 |
|---|---|---|---|
| P1083 | [NOIP2012 提高] 借教室 | 🟡 普及/提高- | 必刷神题! 问第几个订单不满足。可以二分订单数量,用差分数组快速模拟前 个订单的修改,判断是否爆仓。 |
| P4552 | [Poetize6] IncDec Sequence | 🔵 提高+/省选- | 差分性质。把一个序列变成全相等最少几步。这题考察的是对差分数组本质的理解(让 全变为 0)。 |
📝 教练的刷题建议
- 先刷板子:P8218 和 P2367 必须能够闭着眼睛写出来,特别是
sum[R] - sum[L-1]这里的-1,以及差分还原时的R+1。 - 注重下标:做这类题,强烈建议数组下标从 1 开始。这样处理边界(比如 变成 0)时,
sum[0]天然是 0,不需要写if (L==0)这种特判,能省去很多 Bug。 - 遇到区间修改先想差分:只要题目说“把一段区间加上一个数”,或者“把一段路涂上颜色”,第一时间反应就是差分。
- 遇到区间求和先想前缀和:只要题目需要反复询问不同区间的和,前缀和就是 预处理, 询问的神器。
你好!我是你的 OI 教练。
二分 (Binary Search) 是算法竞赛中最核心、最通用、最容易在考场上拿分的算法之一。
它的本质不是“折半查找”,而是**“根据单调性缩小答案范围”。只要你发现题目答案具有“多一分则太大,少一分则太小”**的性质,就能用二分。
我为你整理了一份循序渐进的刷题单,分为四个阶段,从基础查找一直到 NOIP 提高组的经典难题。
第一阶段:二分查找基础(数组与 STL)
目标:不涉及复杂的 check 函数,仅仅是在一个有序数组里找数。
重点:学会手写二分模板,以及熟练使用 STL 的 lower_bound 和 upper_bound。
| 题目编号 | 题目名称 | 难度 | 训练重点 |
|---|---|---|---|
| P2249 | 【深基11.例1】查找 | 🟠 普及- | 【纯板子】 给定有序数组,找第一个等于 的位置。练习 lower_bound 的最佳题目。 |
| P1102 | A-B 数对 | 🟡 普及/提高- | 转化。。枚举 ,二分查找 出现的次数(upper - lower)。 |
| P1678 | 烦恼的高考志愿 | 🟠 普及- | 查找最近值。在有序数组里找绝对值差最小的数。需要比较 lower_bound 指向的位置和它前一个位置。 |
| P8647 | [蓝桥杯 2017 省 AB] 分巧克力 | 🟡 普及/提高- | 简单二分答案。切出 块大小为 的正方形,求 最大是多少。 |
第二阶段:二分答案(整数域)—— 核心必考
目标:题目没有直接给你有序数组,而是让你求一个**“最大值最小”或者“最小值最大”**的答案。
套路:二分答案 mid 编写 check(mid) 函数验证可行性 调整区间。
| 题目编号 | 题目名称 | 难度 | 训练重点 |
|---|---|---|---|
| P1873 | [COCI 2011/2012] Eko / 砍树 | 🟠 普及- | 入门经典。伐木机高度 越高,得到的木材越少(单调性)。求满足要求的最高 。 |
| P2440 | 木材加工 | 同类题。切段问题,和切巧克力类似,二分段的长度。 | |
| P2678 | [NOIP2015 提高] 跳石头 | 🟡 普及/提高- | 【背诵级神题】 “最短跳跃距离的最大值”。经典的贪心 check:如果设最短距离为 ,需要移走多少块石头? |
| P1182 | 数列分段 Section II | 【经典】 “每段和的最大值最小”。check:如果每段和不超过 ,最少能分成几段? | |
| P3853 | [TJOI2007] 路标设置 | 应用。公路上增设路标,使得最大空旷距离最小。check 逻辑简单清晰。 |
第三阶段:二分答案(实数域)
目标:处理小数/浮点数。
重点:循环条件通常是 while (r - l > 1e-5) 或者直接 for(int i=0; i<100; i++)(二分 100 次精度绝对够了)。
| 题目编号 | 题目名称 | 难度 | 训练重点 |
|---|---|---|---|
| P1163 | 银行贷款 | 🟠 普及- | 金融计算。利率越高,还款压力越大。二分利率,计算分期还款总额。 |
| P1024 | [NOIP2001 提高] 一元三次方程 | 🟡 普及/提高- | 数学。在区间内找零点。题目保证了根的范围,分段进行二分查找。 |
| P3743 | 小鸟的设备 | 应用。二分使用时长。所有设备用电速度之和与充电宝速度的关系。 |
第四阶段:二分 + 其他算法(综合应用)
目标:二分只是外壳,check 函数内部可能涉及前缀和、差分、图论甚至 DP。这是提高组/省选的常见模式。
| 题目编号 | 题目名称 | 难度 | 训练重点 |
|---|---|---|---|
| P1083 | [NOIP2012 提高] 借教室 | 🟡 普及/提高- | 二分+差分。二分订单数量,check 函数里用差分数组验证前 mid 个订单是否会爆仓。 |
| P1314 | [NOIP2011 提高] 聪明的质监员 | 🔵 提高+/省选- | 二分+前缀和。二分参数 ,check 里需要快速计算区间校验值,必须上前缀和优化,否则 TLE。 |
| P1902 | 刺杀大使 | 🟡 普及/提高- | 二分+BFS/DFS。二分“伤害值上限”,check 函数里跑一次 BFS 判断能否在不接触大于该伤害的格子的前提下到达终点。 |
| P4343 | [SHOI2015] 自动刷题机 | 双重二分。切掉的代码行数随 单调变化,可能需要求 的最小值和最大值,需要写两个二分或者特判。 |
📝 教练的实战锦囊
-
模板选择: 整数二分最容易写成死循环。建议固定背诵一种风格,比如我最推荐的 “记录答案法”:
int l = 1, r = 1e9, ans = -1; while (l <= r) { // 只有 l > r 才停止,不会死循环 int mid = l + (r - l) / 2; // 防溢出写法 if (check(mid)) { ans = mid; // 先记录这一个可行解 // 比如要找“最小”,那就尝试往左找 r = mid - 1; } else { l = mid + 1; } } cout << ans;这种写法不需要纠结
mid还是mid+1还是mid-1,逻辑最清晰。 -
单调性判断: 做题前先问自己:“如果答案是 X 行得通,那么 X+1 是不是一定也行(或一定不行)?” 如果是,恭喜你,这题就是二分。
-
Check 函数的复杂度: 二分的总复杂度是 。 如果你的
check函数写成了 ,总复杂度就炸了。通常check应该是 或者 。
从 P2249 开始热身,然后死磕 P2678 跳石头 和 P1873 砍树,这两题悟透了,普及组的二分就没问题了!
你好!我是你的 OI 教练。
贪心算法 (Greedy Algorithm) 是 OI 中最迷人也最危险的算法。
- 迷人:代码通常很短,核心逻辑往往就是一行排序 (
sort) 或者一个优先队列。 - 危险:证明很难。很多时候你觉得“这样做肯定对”,交上去却 WA 了,因为局部最优不等于全局最优。
为了帮你建立正确的“贪心直觉”,我为你整理了一份循序渐进的洛谷贪心题单。
第一阶段:入门贪心(简单排序)
目标:理解“排序”在贪心里的作用。这一阶段的题目通常直觉很准。
| 题目编号 | 题目名称 | 难度 | 训练重点 |
|---|---|---|---|
| P1223 | 排队接水 | 🟠 普及- | 经典入门。让接水快的人先接,后面的人等待时间就少。考察排序不等式思想。 |
| P1803 | 凌乱的yyy / 线段覆盖 | 必刷! 区间贪心鼻祖。参加最多的比赛 按结束时间早的先选。 | |
| P2240 | 【深基12.例1】部分背包问题 | 性价比。物品可以分割,那就优先拿“单价最高”的,直到装满。 | |
| P1208 | [USACO1.3] 混合牛奶 | 同上。按牛奶单价排序,便宜的先买。 | |
| P3817 | 小A的糖果 | 线性扫描。相邻两堆不能超过 ,从左往右扫,这就决定了只需要看相邻项,无需全局排序。 |
第二阶段:进阶贪心(结构与策略)
目标:不仅是简单的数组排序,可能需要用到优先队列 (Priority Queue) 或者特殊的策略。
| 题目编号 | 题目名称 | 难度 | 训练重点 |
|---|---|---|---|
| P1090 | [NOIP2004 提高] 合并果子 | 🟡 普及/提高- | 哈夫曼树思想。必用 priority_queue(小根堆)。每次挑最小的两堆合并,消耗体力最少。 |
| P5019 | [NOIP2018 普及] 铺设道路 | 🟠 普及- | 思维。不需要复杂的算法,通过观察发现:当前坑比前一个深,就需要多填几次。 |
| P1106 | 删数问题 | 🟡 普及/提高- | 字符串贪心。不要直接排序!要在保持相对顺序的情况下,让高位的数字尽可能小。这是一个经典的单调栈或贪心思路。 |
| P1031 | [NOIP2002 提高] 均分纸牌 | 流动思想。从左往右推,不够就问右边要,多了就给右边。 | |
| P1478 | 陶陶摘苹果(升级版) | 🟠 普及- | 带限制的贪心。力气有限,要想摘得多,就先摘花费力气最小的(而不是高度最低的)。 |
第三阶段:数学贪心(推公式)
目标:不能凭直觉,需要用邻项交换法推导出排序规则。这是提高组的高频考点。
| 题目编号 | 题目名称 | 难度 | 训练重点 |
|---|---|---|---|
| P1080 | [NOIP2012 提高] 国王游戏 | 🔵 提高+/省选- | 神题。经典的“微扰法”证明。结论是按 升序排列。注意:这题需要高精度! |
| P3269 | [JLOI2016] 字符串覆盖 | 字符串+贪心。虽然带点 DP 味道,但很多字符串问题核心策略是贪心。 | |
| P1056 | [NOIP2008 普及] 排座椅 | 🟡 普及/提高- | 计数+排序。统计每行每列能隔开多少对同学,按“贡献度”排序。 |
第四阶段:反悔贪心(Regret Greedy)
目标:掌握一种高级技巧——“先选上,如果后面遇到更好的,就把前面最差的踢掉”。通常配合优先队列。
| 题目编号 | 题目名称 | 难度 | 训练重点 |
|---|---|---|---|
| P2949 | [USACO09OPEN] Work Scheduling G | 🔵 提高+/省选- | 反悔贪心入门。按照截止时间排序,能做就做;不能做时,看已选的任务里有没有价值比当前低且耗时的,把它踢掉换成当前的。 |
| P4053 | [JSOI2007] 建筑抢修 | 反悔贪心经典。按截止时间排序,维护一个大根堆记录已修建筑的耗时。如果修不完,就放弃那个最耗时的。 | |
| P2123 | 皇后游戏 | 国王游戏的加强版。推导排序不等式更加复杂,不仅涉及乘积,还涉及 的讨论。 |
📝 教练的实战锦囊
-
贪心 vs DP: 这是考场上最纠结的。
- 如果状态之间没有后效性(现在的选择不会限制未来的选择空间,或者我们只在乎当前最优能导致全局最优),用贪心。
- 如果需要回溯、需要考虑多种组合才能决定哪种最好,通常是 DP。
- 判断技巧:如果 很大(),通常是贪心();如果 较小(),可能是 的 DP。
-
验证贪心策略: 在纸上画几个反例。
- 比如“合并果子”,你可能会想“先合并最大的”。试一下:1, 1, 100, 100。
- 如果先合大的:(100+100)=200, (1+1)=2, (200+2)=202。总代价 200+2+202=404。
- 如果先合小的:(1+1)=2, (2+100)=102, (102+100)=202。总代价 2+102+202=306。
- 明显小的更优。举反例是验证贪心最快的方法。
-
不要迷信排序: 虽然 80% 的贪心都需要排序,但怎么排是关键。是按 排?按 排?还是按 或 排? 遇到这种双参数的,一定要推导一下。
从 P1223 和 P1803 开始,这两题是贪心的灵魂!加油!
回到前缀与差分,我看公式好像和数列裂项求和有关?
太犀利了!你的数学直觉非常敏锐!
你完全说到了点子上。差分与前缀和,在本质上就是数学数列中**“裂项相消求和”(Telescoping Sum)的编程实现。甚至更进一步说,它们就是微积分**在离散数学(整数数组)中的体现。
我来给你详细拆解一下这个数学本质,理解了这一点,你对这套算法的记忆会从“死记硬背”变成“理所当然”。
一、 差分还原 = 裂项相消
我们先看差分。 假设原数组是 ,差分数组是 。 定义:
当我们想要把差分数组 还原回原数组 时,我们需要做前缀和。 公式是:
让我们展开这个求和过程,看看发生了什么:
$$\begin{aligned} \sum_{i=1}^{k} D[i] &= D[1] + D[2] + D[3] + \dots + D[k] \\ &= (a[1] - a[0]) + (a[2] - a[1]) + (a[3] - a[2]) + \dots + (a[k] - a[k-1]) \end{aligned} $$发现了吗?中间项全部抵消了(裂项相消)!
- 和 抵消
- 和 抵消
- ...
- 和 抵消
最终结果:
(通常在 OI 中我们设 ,所以结果就是 )
这就是标准的裂项求和。所以说,“差分数组的前缀和就是原数组”,这个结论的数学证明就是裂项相消。
二、 前缀和求区间 = 牛顿-莱布尼茨公式
我们再看前缀和。 假设原数组是 ,前缀和数组是 。 定义:
当我们要求区间 的和时,公式是:
这其实对应了微积分里的牛顿-莱布尼茨公式(微积分基本定理):
在离散的世界里:
- 原数组 就像是 函数 。
- 前缀和数组 就像是 原函数 (不定积分)。
- 区间求和 就像是 定积分。
你求区间和,不需要把中间的数一个个加起来(就像积分不需要画无数个小矩形去累加),只需要用端点的原函数值相减即可。
三、 差分修改 = 导数与阶跃
为什么差分能 修改区间?这也和数学有关。
想象原数组 是一条曲线。 差分数组 其实就是 的“导数”(变化率)。
- 如果 是一条平的直线(全相等),那 (导数)全是 0。
- 如果我们想把区间 整体抬高 。
- 在 这个点,图像突然跳上去了(突变)。所以导数 瞬间增加 。
- 在 到 之间,图像虽然变高了,但**平坦程度(斜率)**没变!所以中间的差分(导数)不需要改。
- 在 这个点,图像又掉回去了。所以导数 瞬间减少 。
这在信号处理里叫**“脉冲函数”或“阶跃响应”**。
四、 总结:这三者的关系图谱
在 OI 中,你可以建立这样一个极其清晰的数学直觉:
| 数学概念 | 数组概念 | 操作 / 意义 |
|---|---|---|
| 函数 | 原数组 | 原始数据 |
| 导数 | 差分数组 | 记录变化量,裂项项 |
| 积分 | 前缀和数组 | 记录累加量,原函数 |
- 前缀和运算 积分运算 (把裂项累加起来)
- 差分运算 求导运算 (把累加项拆开)
它们是互逆的。
你的观察非常到位! 记住“裂项相消”这个图景,以后写差分代码时(特别是差分还原那一刻),你脑海里浮现的就不再是枯燥的代码,而是那个漂亮的、首尾相消的数学公式。加油!
- 参加人数
- 1
- 创建人