(一)图论
最短路
dijkstra
复习一遍模板。Dijkstra模板
适用性:适用于非负权图。
每个点第一次从堆中被取出时,其 \(dis\) 一定是最短路。
\(Dijkstra\) 贪心的正确性:
现证明:取出 \(x\) 时,它的 \(dis\) 已经是最短路。
反证法:若 \(x\) 的 \(dis\) 不为最短路,其更新路径有两种方式,即由堆里扩展而来或由外部点扩展而来。
-
由堆中点扩展而来,由于 \(dis[x]\) 的最小性,所以无论堆中的点怎样扩展,都不能小于 \(dis[x]\)
-
由外部点扩展而来,要到达外部点,首先必须得由堆中点扩展而来,所以也不存在小于 \(dis[x]\) 的可能。
综上所述,点 \(x\) 第一次从堆中被取出的时候,其 \(dis\) 一定是最短路。
例题 \(1.1.1\):海滩防御 - 洛谷
\(dijkstra\) 算法的扩展应用:
- 求最短路上的最大边权。
具体实现与 \(Dijkstra\) 差别不大,每次更新时用边权更新 \(dist\) 数组即可。
int z = std::max(edge[i], dist[x]);
if (z < dist[y])
dist[y] = z, q.emplace(-d[y], y);
例题 \(1.1.2\):通往奥格瑞玛的道路 - 洛谷
\(Dijkstra\) 加二分,易发现答案具有单调性,二分答案,\(Dijkstra\) 判断可行性。
例题 \(1.1.3\):[JLOI2011] 飞行路线
分层图最短路模板。
思路:将一个节点扩展成 \(k\) 个节点,每个节点与相应的其它节点称为一层,每层之间连边权为零的边即可。
更简洁的,可以用 \(dp\) 实现。
例题 \(1.1.4\):[GXOI/GZOI2019]旅行者 - 洛谷
比较巧妙的一道题,或许可以称作多源最短路?
在原图和反图上分别跑一遍最短路,分别求出,兴趣城市到其它所有点的最短距离和其他点到兴趣城市的最短距离。
初始时,将所有兴趣城市入队。(正确性:想象一个虚拟原点,其与每个兴趣城市都有一条边权为 \(0\) 的边)
最终答案为 \(\min_{u,v}\{df(u)+ds(v)+edge(u,v)\}\)
\(df, ds\) 分别表示兴趣城市到其它点的最短距离和其他点到兴趣城市的最短距离。
例题 \(1.1.5\):[ICPC-Beijing 2006] 狼抓兔子 - 洛谷
例题 \(1.1.6\):速度限制 - 洛谷
类似于分层图最短路,可以用 \(Dijkstra\) 配合 \(DP\) 实现,设 \(F(i,s)\) 表示到达点 \(i\) 时,速度为 \(s\) 的最短路。
转移即可。
spfa
\(SPFA\) 与负环:
定理 \(1.2.1\) :若最短路边数大于 \(n-1\) ,则整张图存在负环( \(n\) 为图的点数)。
判断负环:具体地,记录 \(c_i\) 表示从源点到 \(i\) 的最短路边数,松弛时,令 \(c_y=c_x + 1\) ,并判断 \(c_y\) 是否大于 \(n-1\) 。
复习一下:spfa 模板
例题 \(1.2.1\):跑路 - 洛谷
结合倍增考察。
预处理出每两个点是否只需一次跑路可以到达,连边后跑 \(SPFA\) 即可。
具体地:设 \(F(i,j,k)\) 表示 \(i, j\) 之间是否存在一条长度为 \(2^k\) 的路径,用 \(F(i, j, k) = (F(i,t,k-1)~\mathrm{and}~F(t,j,k-1))\) 。
Floyd
\(floyd\) 很短很好记忆,关键代码仅有一行。
d[i][j] = std::min(d[i][j], d[i][k] + d[k][j]);
\(Floyd\) 与传递闭包:
传递闭包:已知一些元素之间的关系,且满足关系具有传递性,求出尽可能多的元素之间的关系。
\(Floyd\) 求解传递闭包问题只需将内层操作改为 \(f(i,j)=f(i,j)~\mathrm{or}~(f(i,k)~\mathrm{and}~f(k,j))\) 即可。
\(Floyd\) 求传递闭包的 \(bitset\) 实现:
std::bitset<Maxn> q;
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
if (f[i][k]) f[i] = f[i] | f[k];
时间复杂度:\(O(\frac{n^3}{w})\) ,\(w\) 是一个常数,约为 \(64\) 。
实际应用:求出在图中点 \(i,j\) 是否可达。
例题 \(1.3.1\):[USACO07MAR]Ranking the Cows G - 洛谷
相当于给定一张有向图,问最坏情况下加几条边才能使它联通。
\(Floyd\) 跑传递闭包即可。
例题 \(1.3.2\):灾后重建 - 洛谷
数据范围 \(n=200\),稠密图考虑 \(Floyd\) 算法。
每个点修复的时候,就以它为中转点去更新其他的点。在每个时间点上,求取的就是目前修复的所有点之间的最短路。
差分约束
差分约束是解决形如 \(x_1-x_2\le c\dots\) 的不等式的问题。只要 \(x_1,x_2\) 同在某一侧时符号不同,那么所有限制都可以化为形如 \(x_1-x_2\le c\) 的形式。
移项发现 \(x_1\le x_2+c\) 很像最短路中的三角形不等式,考虑建图,建一条 \(x_2\to x_1\) 边权为 \(c\) 的边,拟定一个超级源点 \(s\),由 \(s\) 向 \(x_1,x_2\dots\) 连边后跑 \(spfa\),则 \(dis[1],dis[2]\dots\) 就是原不等式组的一组可行解。无解判定:用 \(spfa\) 判负环。上述过程等价于开始时就将每个点入队,赋 \(dis[i]=0\) 。
差分约束的解的问题:
设超级源点向每个点连的边的 \(w\) ,那么求取的解 \(x_1, x_2\dots x_n\) 就是 \(\le w\) 的最大解,即每个变量取能取到的最大值。
证明:
想象由超级源点 \(0\) 连向所有点的最短路径树,考虑任意一条树边 \(v\to u\) 的意义,即限制 \(x_u\le x_v+c\) 。
引理:对于每条树边,都有 \(x_u=x_v+c\)
若 \(x_u>x_v+c\) ,那么 \(x_u\) 还存在更小的解,不满足最短路的定义。
若 \(x_u < x_v + c\) ,那么 \(x_u\) 的最短路不由 \(x_v\) 贡献而来,不应该出现树边 \((u,v)\)。
故该引理正确。
由如上引理可知,对于每个 \(u\) 都将上界 \(x_v+c\) 卡满,所以求取的解就是 \(\le w\) 的最大解。
如果要求 \(\ge w\) 最小解的话:
若原来是 \(x_u\le x_v+c\) 则变换为 \(x_v\ge x_u-c\) ,这满足了最长路的三角形不等式,\(dis[u]+w(u,v)\le dis[v]\) ,建边后跑最长路即可。
例题 \(2.1\):【模板】差分约束算法 - 洛谷
交了很多遍,比较艹。
例题 \(2.2\):赛车游戏 - 洛谷
太巧妙啦。去掉不在 \(1\to n\) 任何一条路径上的点之后,可以发现,原问题等价于在 \(1\) 到任意一个点的所有路径距离相等。设距离为 \(dis\) ,那么 \(dis(u) + w(u\to v) = dis(v)\) ,代入边权 \(1\sim 9\) ,那么 \(1\le dis(v)-dis(u)\le 9\) 。这不就是差分约束能解决的问题吗,于是跑差分约束后 \(dis(v) - dis(u)\) 就是答案。
例题 \(2.3\):