1390 字
4 分钟
贡献法学习笔记
贡献法(Contribution Technique)学习笔记
前置经历
在解决「史莱姆繁殖问题」的加强版时,卡了很久。简单版(O(n) 反向栈)能秒,但困难版要求所有连续子段的源数之和,第一反应是枚举子段,然后发现 O(n²) 必然炸。
和 CherryClaw 对话后,学到了贡献法这一核心思维模式。
贡献法的本质
换一个问题。
- 枚举视角:枚举每个组合(子段、路径、子集…),算它对答案的贡献 → 组合太多
- 贡献视角:枚举每个基础单元(元素、边、匹配关系…),算它出现在多少组合里 → 每个单元 O(1) 或 O(log n)
数学本质——交换求和顺序:
入门例题
例 1:所有子段和之和(最简单)
题目:给定数组 a[1..n],求
枚举视角:O(n³) 或前缀和压到 O(n²)
贡献视角:每个元素 a[i] 在多少子段中出现?
ans = 0for i in range(1, n+1): ans += a[i] * i * (n - i + 1)O(n),一行。
例 2:所有子段最大值之和(单调栈 + 贡献法)
题目:求所有子段的最大值之和。
贡献视角:a[i] 在多少子段里是最大值?
单调栈预处理:找到左边第一个大于 a[i] 的位置 L,右边第一个大于 a[i] 的位置 R。
在 (L, R) 区间内,a[i] 是唯一最大值。子段要满足 a[i] 是最大值:
# 单调栈预处理 L[i] 和 R[i]for i in range(1, n+1): ans += a[i] * (i - L[i]) * (R[i] - i)例 3:史莱姆困难版(载体是匹配关系)
题目:所有连续子段源数之和。
简单版回顾:从右往左栈,v → v+1 匹配,栈大小 = f(l,r)
困难版核心观察:全局匹配边 (i → j) 在任何包含 i 和 j 的子段中都保持有效。
- 子段包含 [i, j] 时:匹配边生效,减少一次源数(少一个初始史莱姆)
- 子段不包含 i 或 j 时:匹配边失效
贡献法:载体不是元素,是匹配边本身。
每条全局匹配边 (i → j) 被多少子段包含?
最终公式:
total = n * (n + 1) * (n + 2) // 6stack = []for i in range(n-1, -1, -1): while stack and a[stack[-1]] == a[i] + 1: j = stack.pop() total -= (i + 1) * (n - j) stack.append(i)贡献法三步法
| 步骤 | 说明 | 本例对应 |
|---|---|---|
| 1. 选载体 | 选什么作为基本单元? | 匹配边 (i → j) |
| 2. 算出现次数 | 载体在多少组合里生效? | i × (n-j+1) |
| 3. 乘贡献值 | 载体每次生效贡献多少? | -1(减少一次源数) |
注意:贡献值可以是负的——本题从总量中「减去」被认领的。
常见载体类型
| 题目 | 载体 | 生效条件 |
|---|---|---|
| 子段和之和 | 元素 a[i] | l ≤ i ≤ r |
| 子段最大/最小值之和 | 元素 a[i](用单调栈找到它为最值的区间) | a[i] 是 [l,r] 的最值 |
| 图上路径边权和 | 边 (u,v) | 路径经过该边 |
| 所有点对距离之和 | 边 (u,v) | 多少点对的最短路径经过它 |
| 子段内逆序对之和 | 逆序对 (i,j) | l ≤ i 且 j ≤ r |
| 史莱姆困难版 | 匹配关系 (i→j) | l ≤ i 且 j ≤ r |
为什么贡献法是分水岭
| 水平 | 面对「所有子段」时 |
|---|---|
| 入门 | 枚举 O(n²),可能超时但能写 |
| 铜牌 | 前缀和、差分、滑动窗口——仍在优化枚举 |
| 银牌 | 先问「能不能不枚举子段?」——切换视角 |
| 金牌 | 灵活选择非常规载体(匹配关系、图论结构…) |
个人反思
卡住的真正原因
不是智商问题,是思维习惯:
- 遇到「所有子段」,第一反应永远是枚举子段——这是本能的、正常的
- 发现枚举会炸后,试图用分治/线段树/双指针「优化枚举」——仍然是枚举视角
- 没有立刻回头找结构不变量(全局匹配边在子段中不变的属性)
改进方向
- 遇到「所有 X 之和」,强制先问:能不能选载体?能不能交换求和顺序?
- 发现枚举炸了,不要继续优化枚举,立刻回头找不变量
- 刷 5~10 道贡献法专题题,让这个思维成为肌肉记忆
和维什戴尔题的呼应
维什戴尔火力覆盖题花了 4 小时才 AC,同样是因为「枚举所有部署 + 所有怪物」的思维惯性。那题虽然没有显式用贡献法,但同样需要反向建图——枚举每只怪物能被哪些部署覆盖,而不是枚举每个部署能覆盖多少怪物。
方向反转是 ACM 中高阶题的核心思维模式,贡献法是其中最重要的具体技法之一。
待刷清单
- CF 所有子段最大值之和(单调栈 + 贡献法)
- CF 所有子段最小值之和(对称)
- 所有子段 gcd / lcm 之和
- 所有子段中逆序对之和
- 图上所有路径的某属性之和
- 更多「所有子段」的贡献法题
相关链接
- 史莱姆简单版:反向栈贪心,O(n)
- 史莱姆困难版:反向栈 + 贡献法 + 组合数学,O(n)
- 维什戴尔火力覆盖:攻击范围建模 + 枚举优化 + 溅射扩散
笔记生成于与 CherryClaw 的对话,2026-06-20
分享
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时
相关文章 智能推荐
