<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>图论 on Zirnc's Blog</title><link>https://blog.chungzh.cn/blog/%E5%9B%BE%E8%AE%BA/</link><description>Recent content in 图论 on Zirnc's Blog</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Sat, 15 Apr 2023 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.chungzh.cn/blog/%E5%9B%BE%E8%AE%BA/index.xml" rel="self" type="application/rss+xml"/><item><title>二分图笔记</title><link>https://blog.chungzh.cn/oi-history/bigraph/</link><pubDate>Sat, 15 Apr 2023 00:00:00 +0000</pubDate><guid>https://blog.chungzh.cn/oi-history/bigraph/</guid><description>定义 在图论中，二分图（bipartite graph）是一类特殊的图，又称为二部图、偶图、双分图。二分图的顶点可以分成两个互斥的独立集 $U$ 和 $V$ 的图，使得所有边都是连结一个 $U$ 中的点和一个 $V$ 中的点。
给定一个二分图 $G$，在 $G$ 的一个子图 $M$ 中，$M$ 的边集中的任意两条边都没有共同的端点，则称 $M$ 是一个匹配。
最小点覆盖：选最少的点，满足每条边至少有一个端点被选。
交错路始于非匹配点且由匹配边与非匹配边交错而成。
增广路是始于非匹配点且终于非匹配点的交错路。
特性 二分图中不存在奇环
因为每一条边都是从一个集合走到另一个集合，只有走偶数次才可能回到同一个集合。
König 定理：一个图是二分图当且仅当它的最小顶点覆盖的顶点数等于最大匹配的边数
首先，最小点集覆盖一定 &amp;gt;= 最大匹配，因为假设最大匹配为 $n$，那么我们就得到了 $n$ 条互不相邻的边，光覆盖这些边就要用到 $n$ 个点。现在我们来思考为什么最小点击覆盖一定 &amp;lt;= 最大匹配。任何一种 $n$ 个点的最小点击覆盖，一定可以转化成一个 $n$ 的最大匹配。因为最小点集覆盖中的每个点都能找到至少一条只有一个端点在点集中的边（如果找不到则说明该点所有的边的另外一个端点都被覆盖，所以该点则没必要被覆盖，和它在最小点集覆盖中相矛盾），只要每个端点都选择一个这样的边，就必然能转化为一个匹配数与点集覆盖的点数相等的匹配方案。所以最大匹配至少为最小点集覆盖数，即最小点击覆盖一定 &amp;lt;= 最大匹配。综上，二者相等。
二分图判定 染色法：用 $1,2$ 两种颜色标记图中的节点，与一个节点相邻的所有节点的颜色必须和它不同，若标记过程中出现冲突，说明图中存在奇环。使用 DFS 实现。$O(N+M)$。
CF687A NP-Hard Problem 二分图判定裸题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #include &amp;lt;bits/stdc++.</description></item><item><title>欧拉回路笔记</title><link>https://blog.chungzh.cn/oi-history/euler-graph/</link><pubDate>Sat, 25 Mar 2023 00:00:00 +0000</pubDate><guid>https://blog.chungzh.cn/oi-history/euler-graph/</guid><description>定义 欧拉回路：通过图中每条边恰好一次的回路 欧拉通路（欧拉路径）：通过图中每条边恰好一次的通路 欧拉图：具有欧拉回路的图 半欧拉图：具有欧拉通路但不具有欧拉回路的图 判定 无向图是欧拉图当且仅当： 非零度顶点是连通的 顶点的度数都是偶数 无向图是半欧拉图当且仅当： 非零度顶点是连通的 恰有 0 或 2 个奇度顶点 有向图是欧拉图当且仅当： 非零度顶点是强连通的 每个顶点的入度和出度相等 有向图是半欧拉图当且仅当： 非零度顶点是弱连通的 至多一个顶点的出度与入度之差为 1 至多一个顶点的入度与出度之差为 1 其他顶点的入度和出度相等 弱连通：将所有有向边替换为无向边后，整张图连通。
Hierholzer 算法 Hierholzer 算法的具体步骤：遍历当前节点的所有出边，并 DFS 访问相邻顶点，将经过的边删掉。遍历完所有出边后，将 $u$ 加入栈中。最后把栈中的顶点反过来，再输出，就是欧拉回路。
如果要求字典序最小，只需在一开始对每个点的所有出边从小到大排序。这样一来，欧拉回路上从左往右看，每个点的后继都取到了理论最小值。
对于无向图和有向图的欧拉路径，必须从奇点或唯一的出度比入度大 1 的点开始 dfs。
Luogu-P7771 【模板】欧拉路径
实现时，要注意一个小细节：用 hd[x] 数组记录节点 $x$ 目前删到了哪条边，每次走过一条边时要 hd[x]++，然后下次到达这个点时再调用这个值。否则的话，每次到了这个点都要从 0 开始判断这些边是否被删除掉，会超时。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include &amp;lt;bits/stdc++.</description></item><item><title>割点和桥笔记</title><link>https://blog.chungzh.cn/oi-history/cut/</link><pubDate>Sun, 19 Mar 2023 00:00:00 +0000</pubDate><guid>https://blog.chungzh.cn/oi-history/cut/</guid><description>定义 若对于无向连通图的一个点 $x$，从图中删去这个点和与这个点相连的所有边后，图不再是连通图，则 $x$ 为这个图的割点。
若对于无向连通图的一条边 $e$，从图中删去这条边后，图不再是连通图，则 $e$ 为这个图的割边（桥）。
求解 无向图的搜索树 从任意一个点出发进行 DFS，每个点只能访问一次，所有被访问过的结点和边构成一棵搜索树。
然后就可以将图上的边分为两类，树边和返祖边，返祖边连接了一个点和它的一个祖先。
时间戳 dfn 和追溯值 low $dfn[x]$ 表示在 DFS 的过程中，$x$ 第一次被访问的顺序。
$low[x]$ 表示 $x$ 和 $x$ 的子树中所有点的时间戳 和 从 $x$ 的子树中的点通过仅一条返祖边可以达到的点的时间戳 的最小值。
更新 $low$ 的方法：
如果 $v$ 是 $u$ 的儿子：$low[u] = min(low[u], low[v])$ 否则：$low[u] = min(low[u], dfn[v])$ 割点的判定 对于某个点 $u$，如果它的儿子中存在一个点 $v$，使得 $low[v] \ge dfn[u]$，即不能回到祖先，那么 $u$ 就是割点。
对于搜索树的根节点就比较特殊，如果它在搜索树中只有一个儿子，是不能成为割点的，需要特判。
树的叶子节点由于没有儿子，也不能成为割点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 void tarjan(int u, int father) { int child = 0; vis[u] = 1; low[u] = dfn[u] = ++inde; for (int i = 0; i &amp;lt; g[u].</description></item><item><title>CF-342E Xenia and Tree - 根号分治</title><link>https://blog.chungzh.cn/oi-history/cf-342e/</link><pubDate>Thu, 06 Oct 2022 00:00:00 +0000</pubDate><guid>https://blog.chungzh.cn/oi-history/cf-342e/</guid><description>CF342E Xenia and Tree
题意 给定一棵 $n$ 个节点的树，初始时 1 号节点为红色，其余为蓝色。
要求支持如下操作：
将一个节点变为红色。 询问节点 $u$ 到最近红色节点的距离。 共 $q$ 次操作。
$1 \le n, q \le 10 ^5$
分析 首先我们有两种暴力思路：
每次将一个点变为红色，就从那个点开始 BFS，更新它周边结点的最小值，直到无法更新。 每次询问，都和之前的红色点求 LCA，计算出距离，再取最小值。 这两种做法都过不了。但我们可以将它们结合起来，这就是根号分治（a.k.a. 操作分块）。
我们把操作序列以 $\sqrt m$ 为块长分块，对于一个询问，有两种情况：
在同一块内且在询问之前的修改，可以暴力 LCA 求距离。 对于之前块的修改，可以在处理完那个块之后，从块中修改的红点开始多源 BFS 更新每个点的答案。 最后答案便是两种情况取最小值。
大概也许是 $O((n+m)\sqrt m)$？其实我不会算，但是挺快的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 #include &amp;lt;bits/stdc++.</description></item><item><title>最短路笔记</title><link>https://blog.chungzh.cn/oi-history/shortest-path/</link><pubDate>Sat, 01 Oct 2022 00:00:00 +0000</pubDate><guid>https://blog.chungzh.cn/oi-history/shortest-path/</guid><description>又是一年 CSP 复赛，已经一年没写过最短路了，赶紧复习一下。
Floyd-Warshall 算法 Floyd 算法是用来求所有结点对最短路的。适用于所有不含负环的图。
这个算法运用了 DP 的思想。首先定义 f[k][i][j] 表示只允许经过结点 $1, 2, \cdots k$，结点 $i$ 到结点 $j$ 的最短路长度。初始化时，f[k][i][i] = 0，其他赋值为 $+\infty$。可以有 f[k][i][j] = min(f[k-1][i][j], f[k-1][i][k] + f[k-1][k][j])（f[k-1][i][j] 表示不经过 $k$ 点的最短路径，f[k-1][i][k] + f[k-1][k][j] 表示经过 $k$ 点的最短路径）。这时可以发现，数组的第一维是可以忽略的，所以直接写成 f[i][j] = min(f[i][j], f[i][k] + f[k][j])。
时间、空间复杂度均为 $O(N^3)$。
实现：
1 2 3 4 for (int k = 1; k &amp;lt;= n; k++) for (int i = 1; i &amp;lt;= n; i++) for (int j = 1; j &amp;lt;= n; j++) f[i][j] = min(f[i][j], f[i][k] + f[k][j]); 无向图的最小环问题 Luogu-P6175 无向图的最小环问题</description></item><item><title>CF-1385E Directing Edges</title><link>https://blog.chungzh.cn/oi-history/cf-1385e/</link><pubDate>Fri, 26 Aug 2022 00:00:00 +0000</pubDate><guid>https://blog.chungzh.cn/oi-history/cf-1385e/</guid><description>CF-1385E Directing Edges
题意 给定一个由有向边与无向边组成的图，现在需要你把所有的无向边变成有向边，使得形成的图中没有环。
如果可以做到请输出该图，否则直接输出&amp;quot;NO&amp;quot;。
分析 我们先只连接有向边，然后做一遍拓扑排序，如果失败了，就说明有环，输出 “NO”。
然后处理剩下的无向边。对于无向边 $(u, v)$，如果 $u$ 的拓扑序小于 $v$，那么令这条边的方向是 $u\rightarrow v$。否则，方向就是 $v\rightarrow u$。因为这条边是从拓扑序小的点指向拓扑序大的点，所以必然不会形成环。
RECORD
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 #include &amp;lt;algorithm&amp;gt;#include &amp;lt;cstdio&amp;gt;#include &amp;lt;cstring&amp;gt;#include &amp;lt;iostream&amp;gt;#include &amp;lt;vector&amp;gt; using namespace std; const int MAXN = 200005; int n, m; vector&amp;lt;int&amp;gt; G[MAXN]; int c[MAXN], topo[MAXN], id[MAXN], t, bn, x[MAXN], y[MAXN]; bool dfs(int u) { c[u] = -1; for (auto v : G[u]) { if (c[v] &amp;lt; 0) return false; else if (c[v] == 0 &amp;amp;&amp;amp; !</description></item><item><title>拓扑排序笔记</title><link>https://blog.chungzh.cn/oi-history/topo-sort/</link><pubDate>Fri, 26 Aug 2022 00:00:00 +0000</pubDate><guid>https://blog.chungzh.cn/oi-history/topo-sort/</guid><description>引入 给定一张有向无环图（DAG, Directed Acyclic Graph），对其顶点进行排序，使得对于每条从 $u$ 到 $v$ 的有向边 $(u, v)$，$u$ 在排序中都在 $v$ 的前面。这种排序就称为拓扑排序（Topological sorting）。
当且仅当图中没有定向环时（即有向无环图），才有可能进行拓扑排序。如果排序失败，就说明该有向图存在环，不是 DAG。
任何有向无环图至少有一个拓扑排序。
举例：在某校的选课系统中，存在这样的规则：每门课可能有若干门先修课，如果要修读某一门课，则必须要先修读此课程所要求的先修课后才能修读。假设一个学生同时只能修读一门课程，那么，被选课系统允许的他修完他需要所有课程的顺序是一个拓扑序。
在这个例子中，每一门课程对应有向图中的一个顶点，每一个先修关系对应一条有向边（从先修课指向需要先修课的课）。
算法 Kahn 算法 初始状态下，集合 $S$ 装着所有入度为 $0$ 的点，$L$ 是一个空列表。
每次从 $S$ 中任意取出一个点 $u$ 放入 $L$, 然后将 $u$ 的所有边 $(u, v_1), (u, v_2), (u, v_3) \cdots$ 删除。对于边 $(u, v)$，若将该边删除后点 $v$ 的入度变为 $0$，则将 $v$ 放入 $S$ 中。
不断重复以上过程，直到集合 $S$ 为空。检查图中是否存在任何边，如果有，那么这个图一定有环路，否则返回 $L$，$L$ 中顶点的顺序就是拓扑排序的结果。
基本上就是 BFS 的框架。
代码实现：
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int n, m; vector&amp;lt;int&amp;gt; G[MAXN]; int in[MAXN]; // 存储每个结点的入度 bool toposort() { vector&amp;lt;int&amp;gt; L; queue&amp;lt;int&amp;gt; S; for (int i = 1; i &amp;lt;= n; i++) if (in[i] == 0) S.</description></item></channel></rss>