【学习笔记】图论

建模专题

二分图

一个二分图满足有一种划分方案使得它节点的被分为两部分,且所有边的端点所在的部分不相同。即每条边都连接两个部分。显然我们给二分图染色,确定一个点所有点都确定。如果在染的时候发现冲突就代表不是二分图。

一张图是二分图的充要条件是其没有奇环。这个很显然吧。

定义一个边集是二分图的匹配,当且仅当边集中任意两条边没有公共端点。即所有边独立。二分图的最大匹配就是最大的匹配数。

增广路:一个二分图的增广路是相对于一个匹配而言的。一条增广路是由匹配边和未匹配边交替出现,并以未匹配点作为开头结尾的路径。此时将匹配和未匹配边取反会发现匹配数加一。那么这条路径称为增广路。

匈牙利算法

我们尝试一个个加入 a 部分的点。假设在加入第 \(i\) 个点前已经是当前的最大匹配。那么 \(i\) 需要一个个问它连的 b 部分的点,如果有未匹配点就直接连。否则一个个问连的点原本连的点,然后再问……最后当发现问完了也没有自己的位置代表连不上了。听上去很抽象。复杂度的话,每一次都会把记忆化清空,所以复杂度是 \(O(nm)\) 的。本质上是寻找增广路的一个过程。

点覆盖

一个图的点覆盖指一个点集使得每条边至少有一个端点在点集内。显然最大点覆盖就是全部点。

二分图的最小点覆盖就是最大匹配数。

证明:

  • 考虑拿出最大匹配中的边 \((x,y)\),显然不同时满足存在 \((x,a),(y,b)\) 使得 \(a,b\) 均为非匹配点,否则存在增广路。

  • \(x,y\) 均不满足上述条件,则任选其一加入点集,否则选择满足上述条件的点加入点集。最后选出的点集一定是一个点覆盖。所以最小点覆盖一定不超过最大匹配。

  • 由于任何一组匹配边的端点都需要出现在点覆盖中,所以最小点覆盖一定不小于最大匹配。

  • 所以最小点覆盖等于最大匹配

最大独立集

独立集是指选择一些点集使得这些点之间没有连边。如果我们将一组点覆盖取补集得到的一定是一个独立集,所以二分图最大独立集就是最小点覆盖的补集,即二分图最大独立集 \(+\) 二分图最大匹配 \(=n\)

最小边覆盖

类比最小点覆盖,就是选择一个尽量小的边集使得所有点都被覆盖。存在边覆盖的前提是没有孤立点。二分图最小边覆盖等于最大独立集。

证明:

  • 一条边无法同时覆盖独立集中的两点,所以最小边覆盖不小于最大独立集。

  • 构造等于最大独立集的方案是简单的。选择所有匹配边以及对于每个非匹配点选择任意一条邻边即可。

Hall 定理

若二分图左右点集分别为 \(S_1,S_2\),令 \(|S_1|\le|S_2|\),其存在 \(S_1\) 的完美匹配的充要条件是令 \(N(S)\) 表示点集 \(S\) 的相邻点,有 \(\forall T\subseteq S_1\),满足 \(|T|\le|N(T)|\)

证明:

  • 必要性显然,若可选集合比待选集合小一定不存在完美匹配。

  • 充分性考虑若满足 Hall 定理但不存在完美匹配。此时我们在最大匹配中选择一个非匹配点,考察其连出去的一条边。由于题设此时不存在增广路所以其一定连的是匹配点。连上这个匹配然后对连过去的匹配点原来的匹配对象重复上述操作,由于 \(|T|\le|N(T)|\) 一定会存在一个左部点连到右部的非匹配点,于是此时便与题设冲突,充分性证毕。

二分图如何判断一个点是否必定存在于最大匹配中

额你把可能不存在于最大匹配中的点找出来,然后它连的匹配点的匹配点也可以作为可能不存在于最大匹配中的点,然后这么深搜就好了。

我也不知道正确性啊,反正 这题这么过了

正则二分图匹配

若一个二分图左右部点数相等,且每个点度数均为 \(d\),则称该二分图为正则二分图。

有一个期望复杂度 \(O(n\log n)\) 的做法,流程如下:重复 \(n\) 次选择随机一个未匹配左部点,沿增广路随机游走(即左往右随机走未匹配边,右往左走匹配边)直到走到一个右边的匹配点。将中途的环去掉后增广这条增广路。

复杂度证明:

  • \(M(p)\) 表示 \(p\) 的匹配点,\(b(p)\) 表示增广过程中访问到 \(p\) 期望要从右往左走多少次。

  • 对当前点 \(p\) 分类讨论:若 \(p\) 为右部未匹配点,则

网络流

具体定义和理论什么的看上面博客就好了。这里只放自己需要的板子:dinicISAP无源汇上下界可行流有源汇上下界最大流有源汇有上下界最小流最小费用最大流

注意由于编号方便写成 \(x\)\(x\oplus1\) 的形式所以边的编号从 \(2\) 开始。

不连通大多数情况下应该是不需要判的吧,因为这相当于你两条边之间有一条容量为 \(0\) 的边应该是一样的。

关于 dinic 其判断是否仍然存在增广路时会执行的 bfs 在遇到汇点会直接返回,其具有正确性的原因是该次 bfs 实际上是把这个图重标号成分层图,由于 bfs 的性质在扩展这一层之前的所有层已经扩展完毕,该层及该层往后的所有层都无法扩展该层,于是提前退出的正确性就显然了。

关于最小割的方案输出,首先满足最小割的定义的一个很重要的性质是一个点要不源点可达要不汇点可达,即不会存在两条边使得这两条边割完之后出现三个不连通点集(假设网络流一开始连通),于是我们可以 dfs 或 bfs 每次走没有被割(没有流满)的边,遇到的第一条流满的边就割,这种方案一定是一种最小割方案。

参考 该处提交,dinic 与 isap 均无法做到每次增广的流量不递增,可以大胆推断其不具有凸性,有点懒就不试了。

但是最小费用最大流满足每次增广的花费不递减。费用流每次增广的单位花费其实是固定的,因为你处理的那个 \(dis\) 是固定的,所以你的花费就是固定的流量乘上 \(dis\),我竟然没有意识到……

ISAP

上面的博客没有给出 ISAP 的具体方式。实际上这个算法一定程度上优于 dinic 且同样的上界极松,在大多数不会也不应卡网络流的题目中跑得比理论上界更优的 HLPP 更快。其具体的优化方式是优化了每次寻找增广路后 dinic 需要进行一遍 bfs 寻找是否能产生新的增广路。

与 dinic 相同地在一开始通过 bfs 处理分层图,但不同的是我们在反图上从汇点开始处理分层(这里很奇怪的是如果仅从汇点开始分层但不跑反图仍然是正确的,只是会比跑反图稍慢一点,不清楚是否本自身影响正确性。在反图上分层的原因是此时最短路若要升层则只会升一层,可以配合手玩理解)。然后同样地是 dinic 的基本操作,与 dinic 不同的是,我们并不会重新对图上的点分层,而是在增广的过程中就完成此过程。具体地,在一个点增广完后,其深度(层数)应该是其最小出边的深度加一。而由于最短路的连续性实现时我们只需要其对应深度自增即可。ISAP 同样可以使用当前弧优化,需要注意的是如果此次退出是因为流量不够则不能进行升层的过程。容易发现 \(d_{Ed}\ge n\) 时图中不再存在增广路,这也是该算法的结束条件。

ISAP 同时存在一个优秀的 GAP 优化。如果在一次更新后处在某一个曾经有点的深度的点数为 \(0\) 代表此时残量网络出现断层,可以直接退出增广,因为不会再出现任何增广路,实现时可以通过将 \(d_{Ed}\leftarrow n\) 来实现,因为不会再出现增广路,此时得到的答案就是最后的答案。

最小割树

没什么用的东西。任取两点 \(x,y\) 求最小割 \(c(x,y)\),原图上找到割完后 \(x\) 所处点集 \(V_1\)\(y\) 所处点集 \(V_2\),必有 \(\forall a\in V_1,b\in V_2,c(a,b)\le c(x,y)\)。这个很好说明,因为若 \(c(a,b)>c(x,y)\),那你割掉 \(c(x,y)\) 之后 \(a,b\) 仍然连通。通过这个性质我们可以得到以下做法的正确性:

对于一个点集 \(V\),任取 \(x,y\in V\),求其最小割 \(c(x,y)\),找到原图中分出的两个点集 \(V_1,V_2\),把点集 \(V\) 中的点按照割完后被分的情况分成 \(A,B\) 两个点集,\(A,B\)\(V\) 连一条边权为 \(c(x,y)\) 的边,然后递归处理 \(A,B\) 子问题。容易发现根据上面的性质,任意两点 \(a,b\) 的最小割即其在树上的路径上最小边权。

网络流题目中 \(n\) 一般很小,我们不采用建出树的方法,具体地,在分完点集 \(V_1,V_2\) 后就把 \(V_1,V_2\) 中的点两两更新最小割,直接用二维数组保存任意两点间最小割即可。

模拟费用流

如果一个题目可以使用费用流但是范围很大,可以尝试观察一次增广取得的路是否带有一定的性质使得我们可以用数据结构维护一次增广来模拟费用流的步骤。

欧拉图

欧拉图指具有欧拉回路的图。欧拉回路指一条经过图中所有边恰好一次后能够回到起点的路。

半欧拉图指仅具有欧拉通路但不具有欧拉回路的图。欧拉通路指一条能够经过图中所有边恰好一次的路。显然一个图若存在欧拉回路就存在欧拉通路。

欧拉通路一般也叫做欧拉路径,欧拉回路是一种强限制的欧拉路径。

判定欧拉图可以参考小学奥数。

无向图是欧拉图当且仅当有边连接的点连通且每个点度数皆为偶数。

无向图是半欧拉图当且仅当有边连接的点连通且恰好有两个奇度顶点。

有向图是欧拉图当且仅当每个顶点入度和出度相等。

有向图是半欧拉图当且仅当至多一个顶点出度比入度多一,至多一个顶点入度比出度多一,其余顶点入度和出度相等。

求法:

有向图:遍历当前节点 \(u\) 的所有出边 \((u,v)\),若未走过,那么向节点 \(v\) 搜。遍历完所有出边后,将 \(u\) 加入路径。最终得到的就是一条反着的欧拉路径。倒过来即可。

无向图 基本类似,遍历未访问的边,仍然在结尾加入这个点。

背一下吧不想证明了,应该挺好记的。

一个欧拉图取出 \(n-1\) 条节点的最后一条出边可以得到一棵根向树。相反地也有一个欧拉图的每个点都以一个根向树上的边为最后一条出边时,其他边无论怎么走都可以形成一个欧拉回路。

tarjan

dfs 搜索树(dfs 生成树)

前置知识。

对图中任意一个点开始访问,保留每个点第一次被访问经过的边构造一棵树。剩下的边可以分为三类:返祖边,即某个节点往祖先节点连的边。前向边,即某个节点往子树中非子节点连的边。横插边,即某个节点往与自己没有祖先后代关系的已访问过的节点连的边。

强连通分量

在有向图中,一个强连通分量是一个点集,满足点集中的任意两点互相可达。强连通分量可以用其中任意一个节点表示(类似并查集的表示方法),也可以新给一个编号。

先构建一棵图的 dfs 搜索树。对于搜索树中的节点 \(i\),如果它是它所在的强连通分量在搜索树中访问到的第一个节点,那么其他的节点都在它的子树内,即该节点为深度最低点。可以考虑反证。加入有节点 \(u\) 也在强连通分量中,那么一定有一条路径从 \(i\) 指向 \(u\),但是从 \(i\) 子树出去的边只有可能是返祖边或者横插边,硬性要求通向的节点被访问过,与 \(i\) 是第一个被访问的节点不符。

在一遍 dfs 过程中,我们考虑用栈存被访问过的还没有处理强联通分量的点。同时定义 \(dfn_i\) 表示 \(i\) 的 dfs 序,\(low_i\) 表示在 \(i\) 的子树中,可以回溯到的最早的(即 \(dfn\) 最小的)节点。对于一个强连通分量的第一个点 \(u\),一定有 \(dfn_u=low_u\),因为 \(u\) 为此时的树根。于是若在搜索过程中有 \(dfn_u=low_u\) 我们把 \(u\) 及上面的所有点划作一个强连通分量。

对于一条边 \((x,y)\),现从 \(x\) 访问到 \(y\)。如果 \(y\) 未被访问,代表 \(y\) 处于 \(x\) 子树中,搜索 \(y\),并根据定义让 \(low_x\leftarrow low_y\)。如果 \(y\) 被访问过但是不在栈中了,根据定义代表 \(y\) 的强连通分量已经处理完,无法到达 \(x\),不用管这种情况。如果 \(y\) 被访问过然后在栈中,那么 \(y\) 对于 \(x\) 可达,由于我们并不确定 \(low_y\) 是否仍然在栈中,用 \(dfn_y\) 更新 \(low_x\)。注意这并不会影响正确性,因为如果更新,更新完之后 \(dfn_x\) 一定不等于 \(low_x\),也就不会影响强连通分量的判断。其对其他节点造成的影响同理。

缩点

将有向图中的强连通分量缩成一个点,因为强连通分量中的点互相可达,所以单纯从图的形态表示上是没有问题的。

任何一个有向图缩点后得到的结果都是一个 DAG。

做法就是把所有强连通分量找出来,然后根据原图的连边把不同强连通分量的连边保留即可。

割点

对于无向图来说,割点指将这个点和与这个点相连的边删去后图不连通的点。

仍然考虑 \(dfn_p\)\(low_p\),由于是无向图,所以这里 \(low\) 的定义可以简化为不经过父亲得到的最小时间戳。

对于节点 \(p\),若其儿子中有节点 \(x\) 使得 \(low_x\ge dfn_p\),即无法走到这棵子树外面,就代表 \(p\) 是割点。

但是根节点 \(t\)\(dfn_t=1\),任何节点都满足前置条件,此时我们考虑搜索树的性质,若 \(t\) 不是割点,那么 \(t\) 到达的第一个儿子 \(e\) 可以在不经过 \(t\) 的情况下走过其他的所有点,即其他的所有点都无法成为 \(t\) 的儿子,所以若根节点为割点,其一定满足在 dfs 搜索树中至少有两个直接儿子。

割边也叫做桥,没有根节点的特殊判断。

点双

一个图被称作点双,即点双联通图,当且仅当其中所有点都不是割点。

其实原定义是一个点双中任意两个点都至少有两条边不相交的路径可达。这种定义几乎是等价的,在处理一些题目中用该种思想会更好处理。唯一的不同应该是按照上面给出的定义单独一条边可以划分为一个点双而依照这个定义这条边不属于任意一个点双。

一个无向图中,极大的点双被称为一个点双联通分量,对于极大的定义,就是除自己外没有满足条件的子集包含自己。一个点可能出现在多个不同点双中,但是两个点双一定只有最多一个公共点,此时该点为这两个点双合起来的图的割点,这个显然。对于一个点双,其 \(dfn\) 最小的节点一定是图中的割点或者搜索树的树根。画个图就理解了。

如果这个点是割点,首先此时不能往父亲那边划点,显然两个子树不会划分到同一个点双中,那么割点割的方向的子树中的所有未划分的点归到一个点双,并把除了割点以外的所有点归为已经被划分了。注意到由于子树内

这一步可以用栈解决。如果这个点是搜索树的根,判断其是否为割点,如果是割点按照割点处理,并且每一个子树都是一个割的方向。如果只有一个子树,可以理解其为点双的树根,也是按照割点处理。如果没有子树,即为单点,单独算作一个点双。所以你只需要特判是一个单点的情况就好了,没有必要特殊处理树根。

有一个性质就是如果一条边可以被称作点双的话,对于一个存在多个点双的图,一个原图的割点至少存在于两个点双里面,因为你一个割点至少会割出两个连通块,而任意一条边都会存在且仅存在于一个点双中。

边双

类似点双,一个边双图不存在割边,类似割边比割点好求,边双也比点双好求,把割边删掉后,每一个连通块为一个边双。感性理解。

圆方树

能够将一般无向联通图转化为树形结构处理一些问题。注意开二倍空间。

做法很简单,对于每个点双 \(\{a_1,a_2,\cdots,a_m\}\) 新建一个点 \(p\) 表示,我们称原图中的点为圆点,新建的代表点双的点为方点。删去原图中所有边,连接 \((p,a_1),(p,a_2),\cdots,(p,a_m)\) 即一个方点跟它对应的点双包含的圆点连边。根据点双和割点的性质,任意两个节点之间只会存在一条简单路径,否则可以通过这两条简单路径把点双合并,所以很显然形成的新图是一棵树,称为圆方树。

根据构造方式很容易看出圆方树上的任意路径均呈圆方交替排列,且叶子节点均为圆点。同时连接两个方点的圆点为两个方点对应的点双的公共点,即从一个点双走到另一个点双的 必经点

圆方树的题目有一种很经典的处理技巧:统计路径时为节点赋上合适的权值。如我们需要统计原图上 \(x\)\(y\) 可能经过的点的个数,将方点权值赋为对应点双大小,圆点权值赋为 \(-1\)。此时从一个方点走到另一个方点得到的权值即为两方点点双大小和减去公共点,此时我们统计 \(x,y\) 圆方树上路径点权和加上起点终点多减的 \(2\) 即得到所求。同理我们可以用圆方树处理无向图两点间路径的一些问题。

同余最短路

一个比较经典的问题:给定 \(a_1,a_2,\cdots,a_n\),求 \(\sum x_1a_1+x_2a_2+\cdots+x_na_n\) 中任意 \(i\)\(x_i\ge0\) 且为整数,问凑出多少数 \(b\) 满足 \(b\le R\)

这个式子长得很工整,所以我们考虑把它拆成一个不工整的式子,记可以凑出来数字 \(x=ka_1+t\),相当于我们用 \(a_2,a_3,\cdots,a_n\) 凑出来余数 \(t\)。把所有能凑出来的数按照模 \(a_1\) 分组,记 \(f_i\) 表示得到余数 \(i\) 凑出的最小的数是多少,则容易得出第 \(i\) 组的大小为 \(\lfloor\frac{R-f_i}{a_1}\rfloor+1\)。发现我们需要凑出最小数,往最短路方向思考,即对于每一个余数 \(i\) 都向 \((i+a_2)\bmod a_1,(i+a_3)\bmod a_1,\cdots,(i+a_n)\bmod a_1\)\(a_2,a_3,\cdots,a_n\) 为边权的边跑出的最短路即为解。

这张图是一张完全图且边权并非随机,其是否一定有特殊的性质?重新观察最开始需要求的式子,可以重新联想到完全背包,从而考虑在完全背包中套上图求解。观察加入物品 \(i\) 时余数 \(j\) 的转移路径形如 \(j\to(j+a_i)\bmod a_1)\to\cdots\to j\),其最后一定回到 \(j\) 且这张图被划分成了 \(\gcd(a_1,a_i)\) 个环。对于一个环,由于 \(f_i\) 不仅是背包也是最短路,那么在一个没有负环的图上我们必定不会经过一个点两次,由于我们并不知道环的背包起点在何处,所以我们对于每个环绕两圈得到的一定是最后的答案,于是我们得到了不需要实际建图跑最短路而是利用最短路性质优化完全背包的 \(O(na_1)\) 做法,不难看出若取最小的 \(a_i\) 作为基准物品可以得到最优的时间复杂度。

上述完全背包和最短路做法似乎各有局限性,如完全背包做法似乎无法处理 转移中带乘法 的非传统同余最短路?

Kruskal 重构树

构造非常简单,性质非常优美的良心算法。类比无向图中 kruskal 最小生成树算法,按照边权排序后依次尝试连边,不过在重构树中若我们连接两个不连通的点 \(x,y\),我们会找到 \(x,y\) 的树根 \(fx,fy\),新建节点 \(p\) 并将 \(fx,fy\) 作为 \(p\) 的左右儿子,\(p\) 赋值该条边边权。

假设我们一开始的边权是从小到大排的,那么重构树有几个很优美的性质:

  • 其为一棵恰有 \(n\) 个叶子的满二叉树,共有 \(2n-1\) 个节点,叶子节点全部是原点,非叶子节点全部是新点。
  • 点权满足大根堆的性质。
  • 询问原图上 \(x\to y\) 经过的最大边权最小值即为 \(lca(x,y)\) 的点权。
  • 原图上 \(x\) 通过边权 \(\le k\) 的边可以达到的点在重构树上对应最浅的满足点权 \(\le k\)\(x\) 的祖先 \(t\) 的子树。

Dilworth 定理

对于 \(A\) 上的二元关系 \(R\),若满足:

  • 自反性,对于任意 \(a\) 都有 \(aRa\)

  • 反对称性,对于任意 \(a,b\)\(a=b\)\(aRb\)\(b=a\) 的充要条件。

  • 传递性,对于任意 \(a,b,c\)\(aRb,bRc\)\(aRc\)

则称 \(R\) 为偏序关系。

\(R\)\(A\) 上的二元关系,称 \(B\)\(R\) 中的链,当且仅当对于任意 \(x,y\in B\) 都有 \(xRy\)\(yRx\),称 \(B\)\(R\) 中反链,当且仅当对于任意 \(x,y\in B\) 都有 \(x=y\)\(xRy,yRx\) 均不成立。

\(R\)\(A\) 上偏序关系,\(R\) 中最小链覆盖等于 \(R\) 中最长反链。

删边最短路

处理这种类型的题目:

给定一个无向图,每次可以修改一条边的边权,问最短路。询问互相独立。

首先把最短路上的边找出来。修改分为四类:

  • 不在最短路上,边权变大。显然不会对最短路造成影响。

  • 不在最短路上,边权变小。记修改边为 \((x,y)\) 则若最短路变更一定经过这条边,所以预处理起点终点为根的最短路树即可。

  • 在最短路上,边权变小。显然不会对最短路造成影响。

  • 在最短路上,边权变大。若最短路变更则一定不经过这条边。考虑把最短路拎出来,则任意一条路径均包含最短路上的一段前缀和一段后缀。对于每条非最短路上的边很容易求出经过这条边的最短路和原图最短路中间不同的部分,由于我们只考虑一条边则这条路径一定形如前缀加非最短路边加后缀,把这条路径的权值挂在被跳过的最短路边上。形如一个区间取 max 单点查询,容易做到线段树维护。

posted @ 2024-01-29 13:22  Wind_Leaves_ShaDow  阅读(48)  评论(0)    收藏  举报