2026-06-06 这节课的目标不是把 GESP8 所有内容重新过一遍,而是把两个高频“听过但容易写不稳”的模型讲透:
对应题目:P11966 上学。重点是“多次查询同一学校”怎样转成一次最短路。
对应题目:P3128 Max Flow P。重点是“很多树上路径加一”怎样不用暴力走路径。
每个模型都要讲清适用条件、手推过程、关键代码和易错点。
最短路问题可以理解成:在一张带权图中,从一个起点出发,走到其他点的最小代价是多少。
| 问题类型 | 问法 | 常见算法 |
|---|---|---|
| 单源最短路 | 从一个点到很多点 | Dijkstra / SPFA / Bellman-Ford |
| 全源最短路 | 任意两点之间 | Floyd |
| 最小生成树 | 连接所有点总代价最小 | Kruskal / Prim |
Dijkstra 是一个贪心算法。它每次从还没确定的点里,取出当前距离起点最近的点,然后用这个点去更新邻居。
起点距离为 0,其他点距离为无穷大。
用小根堆快速拿到当前 dist 最小的点。
如果 dist[u] + w < dist[v],就更新 v 的距离。
直到堆为空,所有可达点的最短距离确定。
可以把当前记录的距离看成一根被拉长的橡皮筋。如果发现一条更短的路,就把距离“放松”到更短。
C 城是一个无向带权图。学校在点 s,有 q 个同学,每个同学家在 h_i。要求每个同学从家到学校的最短时间。
| 题面信息 | 含义 |
|---|---|
| 无向图 | 从家到学校 = 从学校到家 |
| 边长 l_i,速度 1 米/秒 | 最短时间就是最短路长度 |
| q 最多 2 × 10^5 | 不能每个同学重新跑一次最短路 |
| 边权为正 | 可以使用 Dijkstra |
s 作为源点跑一次 Dijkstra,得到 dist[x]。每个查询 h_i 直接输出 dist[h_i]。
如果每个同学都跑一次 Dijkstra,复杂度大约是:
q × (n + m) log n当 q, n, m 都是 2 × 10^5 时,这个数量级完全不能接受。
样例中学校是 3,查询是 5, 1, 4。点击按钮观察 Dijkstra 的距离变化。
从学校 3 出发,dist[3] = 0,其他点先设为无穷大。
| 点 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
| dist | ∞ | ∞ | 0 | ∞ | ∞ |
(0,3)。
priority_queue 是大根堆,Dijkstra 需要小根堆。u → v 和 v → u。long long,不要用 int 硬扛。差分是前缀和的逆运算。它的用途是:当我们要对一段区间反复加值时,不直接改整段,而是只在边界做标记。
长度为 6 的数组初始全 0,现在要把区间 [2,4] 全部加 3。
| 下标 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 差分 D | 0 | +3 | 0 | 0 | -3 | 0 |
| 还原 A | 0 | 3 | 3 | 3 | 0 | 0 |
D[l] += k,D[r+1] -= k。最后做一次前缀和还原。
树上差分也是同一个思想,只是“还原”不再是从左到右前缀和,而是从子节点向父节点做 DFS 汇总。
树上点差分解决的问题是:很多次操作,每次把树上一条路径经过的所有点都加 1,最后问每个点被加了多少次。
树上任意两点之间路径唯一。路径 u → v 会从 u 往上走到 LCA(u,v),再往下走到 v。所以 LCA 是两边贡献汇合的位置。
对于路径 u → v 上所有点加 1,设 p = LCA(u,v):
| 类型 | 目标 | 公式 |
|---|---|---|
| 点差分 | 路径上的点加一 | u++, v++, lca--, parent(lca)-- |
| 边差分 | 路径上的边加一 | u++, v++, lca -= 2 |
有一棵树,给出 K 条运输路径 (s,t)。每条路径经过的所有点都加 1。最后输出被经过次数最多的点的次数。
| 题面信息 | 含义 |
|---|---|
| N 个点,N-1 条边,连通 | 这是一棵树 |
| 每次给一对端点 s,t | 一次树上路径加一 |
| K 到 100000 | 不能每条路径都暴力走 |
| 最后只问最大值 | 可以先全部标记,最后一次 DFS 汇总 |
路径 4 → 7,LCA 是 1。点击按钮看差分数组如何变化。
路径是 4-2-1-3-7。我们不沿路加,而是在几个关键点做差分。
| 点 | 4 | 7 | 1 | 0 |
|---|---|---|---|---|
| diff | 0 | 0 | 0 | 0 |
parent[root] 是 0,数组下标 0 要能安全访问。P11966:多次询问到同一个学校,转成“从学校出发的一次 Dijkstra”。
P3128:很多树上路径都要加一,转成“LCA + 点差分 + DFS 汇总”。
| 课堂暴露的问题 | 补题 | 目标 |
|---|---|---|
| Dijkstra 模板不熟 | P4779 / P1339 | 30 分钟内写出堆优化最短路 |
| LCA 不熟 | P3379 | 能解释倍增跳法 |
| 树上点差分公式混乱 | P3128 重写 / P3258 | 能手推点差分公式 |