图论
树分解
对于一个无向图 \(G=(V,E)\),如果有一棵树 \(T\),它的每个顶点是一个 \(V\) 的子集(设第 \(i\) 个点对应的点集是 \(V_i\)),且满足如下条件:
-
\(V_i\) 的并是全集 \(V\);
-
对于每一条边 \((u,v)\in E\),存在一个 \(i\) 使得 \(u\in V_i,v\in V_i\);
-
对于每个原图上的点 \(i\),树上包含 \(i\) 的点构成一个连通块。
那么我们称 \(T\) 是 \(G\) 的一个树分解。一个图显然可能有很多树分解,比如只有一个点 \(V\) 就是一个树分解。
我们希望 \(T\) 中每个点集的大小尽可能更小,例如,树的树分解就可以让每个点集的大小不超过 \(2\)。感性理解,我们可以用最大点集的大小来衡量 \(G\) 和树的接近程度。
定义 \(G\) 的树宽为所有树分解中,\(\max(|V_i|)-1\) 的最小可能值。据说减 \(1\) 其实只是为了把树的树宽凑成 \(1\)。
弦图
定义:一个无向图,满足所有长度不小于 \(4\) 的环内部都至少有一条边将环分割开。这条边就叫做这个环上的一条弦。
这个等价于所有的极小环都是三角形。
弦图有很好的性质,很多一般图上的 NP-hard 问题在弦图上都有线性算法。
弦图的判定
引理 1:弦图的导出子图一定还是弦图。
证明:如果导出子图有长度 \(\ge 4\) 的极小环,那放到原图上一定还是极小环。
对于一个图上的两个点 \(u,v\),定义 \(u,v\) 之间的点割集为所有的点集 \(S\),满足将 \(S\) 内的所有点割掉后,\(u,v\) 不连通。
定义 \(u,v\) 之间的极小点割集为所有极小的 \(S\),也就是不存在 \(T\subset S\) 使得 \(T\) 也是点割集。
下面都将包含 \(u\) 的连通块记作 \(U\),包含 \(v\) 的连通块记作 \(V\)。
引理 2:有一个极小点割集 \(S\)。对于任意的 \(x\in S\),一定存在点 \(x_u\in U,x_v\in V\) 使得边 \((x,u),(x,v)\) 存在。
证明:如果 \(x\in S\) 不满足条件那 \(S-x\) 也是一个点割集,与极小性矛盾。
引理 3:对于弦图的任意一个 \(u,v\) 极小点割集 \(S\),\(S\) 的导出子图一定是团。
证明:任取两个不同的点 \(x,y\in S\),只需要证明边 \((x,y)\) 存在。在 \(U+\{x,y\}\) 内找出来一条 \(x\to y\) 的最短路,设为 \(x\to x_u\leadsto y_u\y\);同理找出来 \(V+\{x,y\}\) 内的最短路。这两条最短路共同构成了一个大小不小于 \(4\) 的环,因此里面必定有一条弦。然而由于选出来的是最短路,这条弦一定不能位于 \(U\) 或 \(V\) 的最短路内;它也不能跨过 \(U,V\),因为是一个点割集;因此只能是 \((x,y)\)。
引理 4:每个弦图 \(G\) 都至少有一个单纯点。此外,若 \(G\) 不是完全图,那 \(G\) 中至少有两个不相邻的单纯点。
证明:
考虑归纳,\(n\le 3\) 的情况都是成立的,以下假设 \(n\ge 4\)。如果 \(G\) 是完全图那结论显然成立。否则,一定存在一对 \(u,v\) 没有直接相连。找出来 \(u,v\) 的一个极小点割集 \(I\)。
考察 \(U' = U\cup I\) 的性质:
- 若 \(U'\) 是团,则取 \(u\) 作为一个单纯点;
- 否则 \(G[U']\) 为不是团的弦图,由归纳假设,至少有两个点 \(x,y\in U'\) 是 \(G[U']\) 的单纯点。由引理 3,\(x,y\) 中至多有一个点在 \(I\) 内,取那个不在 \(I\) 内的点作为单纯点。(这个点只会和 \(U'\) 内的点相连,因此也是原图的单纯点)
同理可以从 \(V'=V\cup I\) 中选出一个单纯点。选出的两个点显然不相邻,于是得证。
定义一个图 \(G\) 的完美消除序列(PEO)为满足如下条件的点的排列 \(V=\{v_1,v_2,\dots,v_n\}\):对于所有 \(1\le i\le n\),\(v_i\) 在 \(v_i,v_{i+1},\dots,v_n\) 的导出子图上是单纯点。
直观理解,也就是依次删掉 \(v_1,v_2,\dots,v_n\),使得删到一个点的时候它在当前剩下的图上是单纯点。
定理 1:一个图是弦图,当且仅当它有完美消除序列。
证明:
- 若 \(G\) 是弦图,则由引理 \(4\),\(G\) 中存在一个单纯点。由引理 1,将这个点删去后得到的 \(G'\) 仍是弦图,归纳得证。
- 若 \(G\) 不是弦图,则存在一个大小 \(\ge 4\) 的环。假设其存在完美消除序列,那么不难发现这个环上的所有点中,在完美消除序列出现的第一个点一定不是单纯点,矛盾。
先定义一些记号,方便下面的叙述。假设有了一个完美消除序列,记 \(pos_x\) 为 \(x\) 在这个完美消除序列中的位置,\(N(x)\) 为 \(x\) 的邻域内 \(pos\) 大于 \(pos_x\) 的点集,\(nxt_x\) 为 \(N(x)\) 内 \(pos\) 最小的点(如果存在的话)。
接下来是判定算法。我们只需要先假设图是弦图,(尝试)求出一个完美消除序列。
算法流程很简单:我们将要倒着构造完美消除序列。假设当前放到完美消除序列里的点集为 \(S\)。对每个节点维护一个 \(cnt_i\),表示它和已经在 \(S\) 内的点中,有多少个连了边。每次选出 \(cnt\) 最大的一个点加入到当前的完美消除序列的开头并更新 \(cnt\)。
用链表分别维护 \(cnt_i=0,1,2,\dots\) 的点是哪些,暴力维护 \(\max cnt_i\),不难证明复杂度是线性的。
如果是弦图,可以证明这个完美消除序列是对的。如果不是弦图,由定理 1,求出来的一定不是完美消除序列。也就是说,我们只需要判断这个完美消除序列是否合法。
暴力判断是 \(O(nm)\) 的,但是利用完美消除序列的性质,对于每一个 \(x\),只需要判断 \(nxt_x\) 是否向 \(N(x)\) 内的每个点都有连边即可。
于是我们在 \(O(n+m)\) 的时间内解决了弦图判定问题。
弦图上的一些经典问题
最大团
引理 5:弦图的极大团一定形如 \(\{x\}\cup N(x)\)。接下来会把这个记作 \(x+N(x)\)。
证明:对于一个极大团 \(Q\),考察 \(Q\) 内 \(pos\) 最小的点 \(x\) 即可。
于是最大团大小即为 \(\max(|N(x)|+1)\)。
极大团
由引理 5,\(x+N(x)\) 不是极大团等价于存在 \(y+N(y)\) 包含了 \(x+N(x)\),我们只需要判断有没有这样的 \(y\)。
容易发现 \(pos_y<pos_x\)。若 \(pos_{nxt_y}<pos_x\),那么取 \(y'=nxt_y\) 仍然满足条件,所以不妨假设 \(nxt_y=x\)。这样就只有 \(O(n)\) 个点对需要判断了。
此时包含等价于 \(|N(y)|>|N(x)|+1\),可以轻松判定。
最小染色数
求出一个完美消除序列,从后往前贪心选能染的最小颜色染上即可。容易发现这样构造的颜色数就是最大团大小,而最小颜色数不可能小于最大团大小。
同理可以求出染色方案数。
最大独立集/最小团覆盖
在完美消除序列上从前往后贪心能选就选。如果选出来的是 \(x_1,x_2,\dots,x_k\),那么 \(\{x_1+N(x_1),x_2+N(x_2),\dots,x_k+N(x_k)\}\) 就构成了一个最小团覆盖。显然最大独立集不能超过最小团覆盖,所以就对了。
Clique-tree
如果一个树分解的每个顶点都是一个极大团,则称它是这个图的团树(Clique-tree)。显然并不是所有图都有团树。
结论:一个图有团树当且仅当它是弦图。特别地,弦图的树宽即为其最大团大小减一。
如何构造团树?一个效率较低的算法是,求出所有极大团,把它们看作点,让两个团之间的边权是交集大小,求最大生成树。
一个线性做法:按照完美消除序列倒序枚举。现在加入 \(p_i\),如果 \(p_i = \{p_i\}\) 那么直接连向树上任意点,否则设 \(p_j\) 是 \(C(p_i) \setminus \{p_i\}\) 中最早出现的点,则 \(C(p_i) \setminus \{p_i\} \subseteq C(p_j)\)。类似判定极大团的方式,如果 \(C(p_i) \setminus \{p_i\} = C(p_j)\) 且 \(p_j\) 所在的点恰好为 \(C(p_j)\),则将 \(p_i\) 加入 \(p_j\) 所在的点,否则新建一个点连向 \(p_j\) 所在的点。
特殊的弦图
区间图
有一些区间,两个区间之间有连边当且仅当它们有交。容易发现区间图是弦图。
区间图的一个完美消除序列是把区间按照右端点从小到大排序。
结论:一个图是区间图等价于它有团树,且团树是一条链。
因此判定一个图是否为区间图,相当于求出所有极大团后解决如下问题:给定一个 \(01\) 矩阵,求能否重排矩阵的列,使得每行的 \(1\) 构成一个连续段?这可以用 PQ tree 做到线性,但是似乎代码实现极其复杂。
子树图
把区间换成了某个树上的若干连通块,仍然是有交则连边。
子树图的一个完美消除序列是选一个根之后按照每个连通块的最浅点从深到浅排序。
结论:子树图等价于弦图。
例题
好像确实有一些应用,对于某些看似不相关的问题可以用弦图的相关理论来分析,降低思考量。
先咕着。
广义串并联图
以下讨论中均假设图连通。
定义
首先来定义基本的二端串并联图。二端指的是有一个源点和一个汇点,而串联/并联是两种对图的合并操作。
-
串联操作:对于两个图 \(G_1(s_1,t_1),G_2(s_2,t_2)\),将 \(G_2\) 直接拼接到 \(G_1\) 上且 \(s_2\) 和 \(t_1\) 重合,并令新图的源汇分别是 \(s_1,t_2\)。
-
并联操作:对于两个图 \(G_1(s_1,t_1),G_2(s_2,t_2)\),将 \(G_2\) 直接拼接到 \(G_1\) 上且 \(s_2,t_2\) 分别和 \(s_1,t_1\) 重合,新图的源汇仍是 \(s_1,t_1\)。
定义二端串并联图为所有能从 \(K_2\) 开始,使用串联/并联操作构造出来的图。
定义串并联图为所有可以通过选择源汇来变成二端串并联图的图。
此外,对于串并联图还有另一种基于收缩的定义方式。一个图是串并联图等价于它可以通过如下操作变回 \(K_2\):
-
叠合重边:若两条边 \(e_1,e_2\) 都连接着 \(u,v\),则将它们替换为一条边;
-
缩二度点:若 \(u\) 的度数恰好为 \(2\),设它连接了 \(x,y\) 两个点,则将 \((u,x),(u,y)\) 替换为一条边 \((x,y)\),并将 \(u\) 删去。
可以证明,一个点双连通图是串并联图等价于它没有同胚于 \(K_4\) 的子图,即不存在四个点 \(a,b,c,d\) 和它们两两之间的六条路径,使得这六条路径除端点外点不相交。
那如果不是点双呢?我们来类似地写出广义串并联图的两种定义。
对于合并,我们添加一种新操作:将 \(G_2\) 拼接到 \(G_1\) 上,使得源点重合,但是不改变图的汇点,\(G_2\) 的汇点也不合并到 \(G_1\) 上(不难发现,此时 \(s\) 是割点)。一个图是广义串并联图当且仅当它可以通过这三种操作生成。
对于收缩,我们也添加一种新操作:缩一度点。意思是,若点 \(u\) 的度数为 \(1\),只连向另一个点 \(v\),那么将点 \(u\) 和边 \((u,v)\) 删去。一个图是广义串并联图当且仅当它可以通过这三种操作收缩成单独一个点。
换句话说,广义串并联图的每个点双都是一个串并联图。这也是等价条件。
于是可以推出另一个广为人知的定义:一个图是广义串并联图等价于它没有任何同胚于 \(K_4\) 的子图。
基本性质
广义串并联图简洁的生成/收缩方式其实本身就是一个很好的性质,之后的例题中会充分体现这一点。
广义串并联图的树宽不超过 \(2\)。
许多结构简单的图,如点仙人掌(还可以任意加一条边),都是广义串并联图。所以有时可以直接无脑套用,或许能减少思考量。
若不允许重边,广义串并联图是稀疏图。更精确地,对于任意的 \(n\ge 2\),都有 \(m\le 2n-3\)。
广义串并联图是平面图,因为 \(K_{3,3}\) 和 \(K_5\) 都包含 \(K_4\) 作为子式。
广义串并联图方法
其实就是用上面提到的三种操作来收缩一个图。收缩过程中,图上的点和边都代表着一个子图,我们可以用收缩过程来 dp。
用一个具体例子来说明。
例题 1:P6790 [SNOI2020] 生成树
题意:给定一个广义串并联图,求它的生成树个数。
对于一条边 \(e\),我们设 \(f_{e,0/1}\) 表示在 \(e\) 对应的子图中,有多少生成森林使得所有点都与 \(e\) 子图中的某个端点连通,且 \(e\) 的两端不连通/连通。
考虑在图收缩的过程中进行 dp,对三种操作分别讨论:
-
缩一度点:假设一度点为 \(u\),唯一的一条边为 \(e=(u,v)\)。此时,\(e\) 所代表的子图必须能够令 \(u,v\) 连通,而 \(e\) 即将被删去,以后也不会再有用,因此我们直接令 \(ans\leftarrow ans\times f_{e,1}\) 即可;
-
缩二度点:假设二度点为 \(u\),两条边分别为 \(e_1=(u,x),e_2=(u,y)\) 且 \(x\neq y\)。此时根据定义,新加入的一条边 \(e\) 的 \(f_{0/1}\) 分别代表通过选择 \(e_1,e_2\) 中的边让 \(x,y\) 不连通/连通的方案数(显然若从 \(e_1,e_2\) 中选出的边无环,那也不会出现跨过 \(u\) 的环):
- 若 \(x,y\) 要连通,那必须都选连通,故 \(f_{e,1}=f_{e_1,1}f_{e_2,1}\);
- 否则为了使 \(u\) 和某个端点连通,一定恰好有一个选择不连通:\(f_{e,0}=f_{e_1,0}f_{e_2,1}+f_{e_1,1}f_{e_2,0}\);
-
叠合重边:设两条边分别为 \(e_1,e_2\),类似缩二度点,分类讨论可以得到 \(f_{e,0}=f_{e_1,0}f_{e_2,0},f_{e,1}=f_{e_1,0}f_{e_2,1}+f_{e_1,1}f_{e_2,0}\)。
初始时设 \(f_{e,0}=f_{e,1}=1\),并且令 \(ans=1\),最后 \(ans\) 即为答案。
一种比较简单的实现:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10, MOD = 998244353;
struct E
{
int f, g;
};
int n, m;
bool vis[N];
map<int, E> g[N];
signed main()
{
cin.tie(0)->sync_with_stdio(0);
cin >> n >> m;
queue<int> q;
auto link = [&](int u, int v, const E& e1)
{
if (g[u].count(v))
{
auto e2 = g[u][v];
g[u][v] = g[v][u] = { (e1.f * e2.g + e1.g * e2.f) % MOD, (e1.g * e2.g) % MOD };
}
else g[u][v] = g[v][u] = e1;
};
auto upd = [&](int u)
{
if (!vis[u] && g[u].size() <= 2) vis[u] = true, q.push(u);
};
for (int i = 1, u, v; i <= m; i++) cin >> u >> v, link(u, v, {1, 1});
for (int i = 1; i <= n; i++) upd(i);
int ans = 1;
while (!q.empty())
{
int u = q.front(); q.pop();
if (g[u].size() == 1) (ans *= g[u].begin()->second.f) %= MOD;
else if (g[u].size() == 2)
{
auto [x, e1] = *g[u].begin();
auto [y, e2] = *g[u].rbegin();
link(x, y, { (e1.f * e2.f) % MOD, (e1.f * e2.g + e1.g * e2.f) % MOD });
}
for (auto e : g[u]) g[e.first].erase(u), upd(e.first);
g[u].clear();
}
cout << ans;
}
复杂度 \(O(n\log n)\)。由于大量使用 map,效率非常低,\(n,m\le 2\times 10^5\) 的数据下在 luogu 上跑了接近 250ms。并且实测改成 unordered_map 后更慢了。
注意虽然复杂度为 \(O(n\log n)\),但是 dp 的合并只会进行 \(O(n+m)\) 次,所以通常瓶颈不在于 dp。
有些问题中,我们需要对点对应的子图也记录一些信息。
例题 2:P10779 小 C 的独立集
题意:给定一个广义串并联图,求最大权独立集。
同样是利用收缩过程 dp。设 \(f_{v,0/1}\) 表示 \(v\) 所对应的子图中 \(v\) 选/不选的独立集的最大权值,\(f_{e,0/1,0/1}\) 表示 \(e\) 所对应的子图中两端分别选/不选时子图内的独立集的最大权值。
转移分讨一下即可。注意一个实现细节:map 中 g[u][v] 和 g[v][u] 存储的答案是不同的,需要区分。
综合练习:公园的 \(Q=0\) 部分分,可以用来练手。
例题 3:[HNOI/AHOI2018] 毒瘤
题意:给定一个无向图,保证 \(m-n\le 10\),求独立集个数。
设 \(d=m-n\)。对一个一般图使用广义串并联图方法收缩的过程中,\(d\) 不会增加,故收缩后的图上仍有 \(m-n\le d\)。由于收缩后的图上最小度数不小于 \(3\),故 \(m\ge \dfrac{3n}{2}\),于是 \(\dfrac{3n}{2}\le m\le n+d\),我们得到 \(n\le 2d, m\le 3d\)。
这意味着对于 \(m-n\) 比较小的图,我们可以先使用广义串并联图方法,然后在收缩后的图上通过指数级复杂度的算法,在 \(O(\mathrm{poly}(n)+\mathrm{exp}(m-n))\) 的复杂度内解决问题。
有向图上也能类似使用上题中的方法。
例题 4:P10044 [CCPC 2023 北京市赛] 最小环
我们类似地发明一下收缩操作:
-
删除入度/出度为 \(0\) 的点;
-
缩入度和出度均为 \(1\) 的点;
-
叠合重边,要求方向也相同。
这是串并联图在有向图上的自然推广。
对于收缩后的图,可以类似分析到 \(n\le 2d, m\le 3d\)。
注意第二个操作中有可能唯一的入边和出边连向同一个点,也就是方向相反的重边,直接缩会带来自环,不过本题中直接计入答案就行了。
例题 5:QOJ7790. 最短路求和
尝试收缩会发现信息没法高效维护。
但是对于这些 \(d=m-n\) 较小的图,我们有另外一种处理方式:注意到度数不小于 \(3\) 的点不超过 \(2d\) 个。把这些点看作关键点,那么图变成了关键点之间挂着若干条链/自环,并且链/自环的条数不超过 \(3d\)。然后对于一条链上的点批量计算即可,可以用双指针什么的优化到 \(O(nd)\)。
操作树和树分解
我们按照如下方式构造一棵广义串并联图的操作树出来:
- 串联(缩二度点):设参与操作的为 \(u,e_1=(u,x),e_2=(u,y)\),把它们对应的操作树节点连到一个新节点 \(e_3=(x,y)\) 上;
- 并联(叠合重边):设两条边分别为 \(e_1=(x,y),e_2=(x,y)\),把它们连到一个新节点 \(e_3=(x,y)\) 上;
- 缩一度点:设把 \(x\) 缩到 \(y\) 上,则将 \(x,y,e=(x,y)\) 连到一个新节点 \(u\) 上,并令 \(y\) 对应的节点变为 \(u\)。
对每个节点 \(x\),设 \(V_x\) 为 \(x\) 的界点集合,也就是说如果 \(x\) 表现为一条边 \((u,v)\) 则 \(V_x = \{u,v\}\),若表现为一个点 \(u\) 则 \(V_x=\{u\}\)。
容易推出:
- 收缩过程中出现的每一个子图都对应操作树上的一个节点;
- 叶子节点恰好是所有的点边,且一个子树内的所有叶子节点就是它对应的子图包含的点和边;
- \(x\) 的子树内和 \(x\) 子树外的边集无交、而点集的交恰好是 \(V_x\)。
而树分解的构造也是类似的。对于每个串联操作 \(x-u-y\),新建一个节点 \(\{x,u,y\}\),将 \(u,(x,u),(y,u)\) 和串联后的节点连上即可;对于并联操作,在两个子图之间连一条边;对于缩一度点,如果把 \(x\) 缩到 \(y\) 上,就把 \(x,(x,y)\) 对应的节点连到 \(y\) 上。
例题 6:QOJ8460. 公园
省流:广义串并联图上动态 dp。
可以把操作树建出来,直接对操作树维护动态 dp。没写过。
例题 7:广义串并联图上最短路查询
是 逛公园 的 \(K=2\) 部分分,不过似乎和正解相差不大。
可以解决任意小树宽图上的最短路查询。设树宽为 \(k-1\),在树分解上分治求解。根据树分解的性质,每个端点不在分治中心同一子树内的路径都一定跨过分治中心的 bag 里的某个节点,预处理 bag 内的 \(k\) 个节点到当前分治的连通块内所有点的最短路即可回答这一层的询问。带上分治,总复杂度 \(O(k(n+m)\log^2 n+q)\)。建出分治树可以单 \(\log\) 在线回答询问。
其实这个思想是经典的,网格图上最短路也是类似的做法:找一个小的点割集,预处理最短路,分治处理。
平面图
好像内容不多。
定义:一个无向图是平面图,当且仅当它能够画在一个平面上使得所有边(除端点外)不相交。
平面嵌入:就是一种把平面图画到平面上的方式。此时平面会被分成若干区域(包括最外面的无限大的面),每个区域被称作一个面。
欧拉公式:对于一个连通的平面图,\(V-E+F=2\),其中 \(V,E,F\) 分别为点数、边数和面数。
由欧拉公式可以推出,简单平面图一定是稀疏图:当 \(n\ge 3\) 时,\(m\le 3n-6\)。
平面图的判定定理:一个图是平面图,当且仅当它没有任何同胚于 \(K_5\) 或 \(K_{3,3}\) 的子图。
平面图的最小割
对偶图:把每个面看成一个点,对于每条原图上的边 \(e\),在和它相邻的面之间连一条边,权值为 \(w_e\)。可能会连出来自环,此时原来的那条边是图上的桥。
可以发现,平面图的对偶图仍是平面图。
我们假设源汇点 \(s,t\) 均在某个平面嵌入的同一个面上。在 \(s,t\) 之间连一条新的边,权值为 \(+\infty\),此时会多出一个面,在对偶图上求出这个新面到原来的外部面的最短路,就是原图的 \(s,t\) 最小割。
如果图的结构已知可以自己编出来对偶图的结构。一般平面图转对偶图也能做,但是这个看起来比较复杂,并且应该没什么用,先咕着。
点双连通、边双联通
下面假设图为无向图,且没有自环。
基本性质:
边双连通(2-edge-connected)
-
边双联通分量是对点集的划分:每个点恰好属于一个边双。
-
将边双缩点后得到一棵树,树上的所有边即为所有割边。
-
在边双连通图上:
- 对于任意点/边,存在包含它的简单环。
- 对于任意两个点,它们之间至少有两条边不相交的路径。
点双连通(2-connected/biconnected)
-
点双连通比边双联通更强。
-
点双连通分量是对边集的划分:每条边恰好属于一个点双。
-
所有点双的点数之和不超过 \(2n\)。
-
可以用圆方树(或类似的 block-cut tree)刻画点双之间的连通性:对每个点双建一个方点,一个点向所有包含它的点双对应的方点连边。
-
在点双连通图上:
- 对于任意两个点,它们之间至少有两条点不相交的路径。
- 对于任意两个点 \(u,v\) 和任意一条边 \(e\),存在从 \(u\) 到 \(v\),且经过 \(e\) 的简单路径。
- 对于任意两条不同的边,都存在一个简单环同时经过它们。
-
边/点双联通分解本质上都是等价关系:两个点若被包含在同一个不经过重复边的环内,则它们属于同一个边双;两条边若被包含在同一个简单环(不经过重复点)内,则它们属于同一个点双。
经过边比经过点更强,因此将边改为点性质也成立;然而通常而言,这两者是等价的,可以将一条边 \((u,v)\) 拆成两条 \((u,x)(x,v)\)(不改变双连通性),此时经过 \((u,v)\) 等价于经过 \(x\)。
寻找割边、割点、求边双/点双联通分量分解都可以 dfs 树上差分,或使用 tarjan 算法线性求出。
耳分解
从图 \(G\) 中选出两个节点 \(s,t\),并添加一条 \(s\) 到 \(t\) 的路径 \(s-p_1-p_2-\cdots-t\),满足 \(p_i\) 互不相同且都不属于 \(G\),得到一个新图 \( G'\)。此时,这条路径称作 \(C_i\) 的一个耳,且若 \(s\neq t\),则称其为 \(C_i\) 的一个开耳。
从一个环 \(C_0\) 开始,每次添加一个耳得到 \(C_{i+1}\),最后得到 \(C_k=G\)。我们称 \(\{C_0,C_1,\dots,C_k\}\) 是 \(G\) 的一个耳分解,特别地,如果每次添加的耳都是开耳,则称它是一个开耳分解。
定理:一个图有耳分解,等价于它是一个边双联通图;一个图有开耳分解,等价于它是一个点双联通图。
证明:
- 左边 \(\implies\) 右边:
归纳证明每一步得到的都是边/点双连通图即可。- 右边 \(\implies\) 左边:
以边双为例,我们给出一个构造性的证明:
任取一棵 dfs 树,取一个包含根的环作为 \(C_0\)。在构造过程中,我们保持当前加入的点集是一个包含根的连通块这一性质。若还有点未被加入,则一定存在一个点 \(u\) 满足 \(fa_u\in C_i,u\notin C_i\)。由边双连通性,\(u\) 子树内一定存在一条返祖边 \((x,y)\) 跨过 \((fa_u,u)\),不妨设 \(x\) 较深,则 \(fa_u\to u\leadsto x\to y\) 是一个耳。点双的情况是类似的,一定存在一个跨过 \(fa_u\) 的返祖边,这样得到的耳是一个开耳。最后把所有剩下的边加进去即可。
上面的证明就给出了一个线性构造耳分解/开耳分解的算法,只需要求出 dfs 树后再对原图进行一遍 dfs 即可。
双极定向
在一个无向图上指定源点和汇点 \(s,t\),能否给每条边定向,使得如下条件成立?
- 定向后的有向图无环,且 \(s\) 是唯一的只有出边的点,\(t\) 是唯一的只有入边的点。
双极定向还有另一种常见的表述(st-Numbering):
- 给定 \(s,t\),将所有顶点排成一个序列 \(p\),使得 \(p_1=s,p_n=t\),且 \(p\) 的每个前缀和后缀的导出子图均连通。
这两个定义等价:后者即是前者的拓扑序。
哪些图有双极定向?
首先,点双连通图上,任意指定源点汇点都可以构造出双极定向:给每个开耳定向即可。
现在我们相当于是在 block-cut tree 上双极定向。如果原图的 block-cut tree 是一条链(圆方树是毛毛虫),那么在两端点的点双中分别指定源点、汇点,此时双极定向也存在;容易发现这也是必要的。换句话说,双极定向存在当且仅当连接 \(s,t\) 之后图是点双连通的。
如何构造双极定向?
我们先求出一棵以 \(s\) 为根的 dfs 树,但是只保留一个点子树中的非树边中深度最浅的那一条,也就是 \(low_u\)。此时,叶子的度数一定是 \(2\)。对于一个叶子 \(u\),只要 \(fa_u\) 或 \(low_u\) 被染色了一定可以立刻染色 \(u\)。考虑对每个点维护一个序列,表示染了这个节点之后,可以依次染序列中的所有点。于是,我们不断剥叶子(不剥去 \(s\) 到 \(t\) 的路径上的点),把它加入到 \(fa_u\) 和 \(low_u\) 的序列的末尾,最后对于 \(s\leadsto t\) 的路径依次递归染色即可。
常用于连通性相关的图论构造,如 景点划分,白鹭兰,Fillomino。
杂项
一般图最大匹配
不会带花树。
首先有一些并不靠谱且无法保证正确性的随机化做法:随机游走找增广路,每次遍历出边时 shuffle。不过想要卡掉似乎要让最大匹配数量较小,这样的话可能对于有性质的图不太好卡?
对于无向图 \(G\),定义它的 Tutte 矩阵 \(A_{ij}\),满足若 \((i,j)\in E\) 则 \(A_{ij}=x_{ij},A_{ji}=-x_{ij}\),这里每个 \(x_{ij}\) 都是一个变量。
定理:\(\det A\neq 0\) 等价于图有完美匹配。
证明:考虑 \(A\) 的行列式展开后是什么:\(\displaystyle\det A=\sum_{p}(-1)^{inv(p)}\prod_{i=1}A_{i,p_i}\),这相当于图的一个有向环覆盖(排列的每个置换环)。注意到如果选出来了一个奇环,那么把环上的所有边反向之后得到的一定和原来贡献恰好相反,所以有贡献的排列实际上环长为偶数。而一个图有完美匹配,等价于它有偶环覆盖:每个偶环选一半的边。
而两倍的最大匹配等于最大的有完美匹配的导出子图大小,也就是 \(A\) 的最大满秩子矩阵大小 \(rank(A)\),这样我们给每个变量独立随机一个值,模一个大素数算一下 rank 就能 \(O(n^3)\) 求最大匹配大小。可以证明错误率不超过 \(n/P\),而 \(n\) 通常很小所以 \(P\) 取 \(10^9\) 左右基本上错误率就可以忽略不计。
如何构造最大匹配?首先我们提取出来一个最大满秩子矩阵且行、列集合相同;容易发现因为有最大匹配,这样的子矩阵必然存在。然后我们依次尝试加入每一条边 \((i,j)\),若 \(A\) 删掉 \(i,j\) 两行两列之后 \(\det\) 仍然非 \(0\),那么一定有 \((i,j)\) 是最大匹配的一个可行边。直接暴力求行列式判断的话是 \(O(n^5)\) 的复杂度。
利用一些线代知识,我们知道 \(A\) 删去每对 \(i\) 行 \(j\) 列后的行列式构成的矩阵就是它的伴随矩阵:\(M_{ij}=(-1)^{i+j}\det A(A^{-1})_{ji}\)。于是只需要判断 \(M_{ij}\) 是否为 \(0\)。而我们知道 \(\det A,(-1)^{i+j}\) 一定不是 \(0\),从而可以直接求逆矩阵做到 \(O(1)\) 判断,这一过程会进行 \(n/2\) 轮因此复杂度是 \(O(n^4)\) 的。
进一步优化,我们需要做的是在删去一行一列后快速维护逆矩阵,事实上这是能做到单次修改 \(O(n^2)\) 的,具体细节可以参考 OI-Wiki,这样重复 \(n/2\) 轮就有 \(O(n^3)\) 的复杂度。
一些拓展:
-
在稀疏图上可以用 Berlekamp-Massey 将计算 \(rank(A)\) 的部分加速到 \(O(nm)\),\(m=O(n)\) 时这相当于少了一整个 \(n\),并且该做法常数不大。
-
如果最大权匹配权值和是 \(O(W)\) 的,那么可以在 \(O(Wn^3+W^2)\) 的复杂度内计算出最大权匹配:给矩阵中每一项附上 \(y^w\) 的系数即可,为了避免多项式计算可以代入 \(W+1\) 个点值然后插值。
度数序列
将一个无向图 \(G\) 的每个顶点的度数按照降序排序,得到的序列称作 \(G\) 的度数序列。
那么问题来了:给定一个单调不增的序列 \(d_1,d_2,\dots,d_n\),如何判断是否存在一个简单无向图的度数序列恰好就是 \(d\)?这样的序列也被称作可图化的(graphic)。
定理(Erdos-Gallai):
一个序列 \(d_1\ge d_2\ge \cdots \ge d_n\) 是 graphic 的,当且仅当 \(\sum d\) 为偶数,且:
- 对于所有的 \(k\in [1,n]\),都有 \(\displaystyle\sum_{i=1}^k d_i\le k(k-1)+\sum_{i=k+1}^n\min(d_i,k)\)。
左边是对前 \(k\) 个点的度数求和,而右边则是一个上界(前 \(k\) 个点内部的贡献加上后 \(n-k\) 个点的贡献),因此这是必要的。不过充分性好像不太好证?
只要判定的话可以随便优化一下做到 \(O(n)\)。怎么构造呢?每次取出来 \(d_i\) 最大的点,向剩下的点中按照 \(d\) 从大到小连恰好 \(d_i\) 条边即可,复杂度 \(O(n\log n + \sum d_i)\)。
考虑另一个情形:给定左部点和右部点的度数,判断是否存在一个二分图满足条件。换句话说,给定每行每列的和,判断是否存在一个 \(01\) 矩阵满足所有限制。
定理(Gale-Ryser):
给定序列 \(a_1\ge a_2\ge\cdots\ge a_n\) 和序列 \(b_1,b_2,\cdots,b_m\),存在一个恰有 \(n\) 个左部点,\(m\) 个右部点的二分图满足条件,当且仅当 \(\sum a=\sum b\),且:
- 对于所有的 \(k\in [1,n]\),\(\displaystyle\sum_{i=1}^k a_i\le \sum_{i=1}^m \min(k,b_i)\).
同样不难发现这是必要的。构造很简单,按照 \(a_i\) 从大到小遍历(这一步好像也可以不要),每次连向 \(b_i\) 最大的几个点就行了。
此外对于一般图还有一些关于必须边和可行边的结论:
对于一个度数序列 \(d\):
- 边 \((i,j)\) 是必须边,当且仅当给 \(d_i,d_j\) 加 \(1\) 后的序列不再合法;
- 边 \((i,j)\) 是不可行边,当且仅当 \(d_i,d_j\) 减 \(1\) 后的序列不再合法。
浙公网安备 33010602011771号