图论(1):连通性问题
严肃抄写 ix35 老师的论文。本来应该边读边记录的,目前还是只读了一半,大概囫囵吞枣的读了无向图部分并且做了部分题目,我还是太菜了 QAQ
1. 连通度与 Menger 定理
对于点 \(u, v\),定义其点割集为删除点集合 \(S\) 内的点 \(u, v\) 不连通,边割集为删除边集合 \(S\) 内的点 \(u, v\) 不连通。定义 \(u, v\) 的点联通度为 \(u, v\) 内最小的点割集的大小,边联通度为最小边割集的大小。称 \(u, v\) 是 \(k\) 点联通的当且仅当其点连通度大于等于等于 \(k\),边连通度同理。一张图的两种联通度定义为其中任意两点的连通度最小值。特别的,\(1\) 点联通和 \(1\) 边联通等价,即“联通”。
Menger 定理:
- 若 \(u, v\) 是 \(k\) 边连通的,那么存在 \(k\) 条从 \(u\) 到 \(v\) 的两两边不交的路径
- 若 \(u, v\) 是 \(k\) 点联通的,那么存在 \(k\) 条从 \(u\) 到 \(v\) 的除了端点外两两点不交的路径。
2. DFS 树
DFS 树本身的常用性质:
- 对于一颗树,那么它 dfs 树中的子树在 dfs 序(下面也称 dfn 序)上是一个区间。
- 对于一张图,dfs 树上只存在树边和返祖边。
DFS 树往往是研究无向图一些连通性的基础。应用很广泛,包括但不仅限于 tarjan 算法,一系列神秘构造题。实际上取出一颗生成树似乎在某些组合构造上面也具有独特的性质。
CF1103C Johnny Solving
对于这种二选一的构造通常都是一定有解啦~欸这题太要脑电波了
建立 dfs 树,如果存在深度大于等于 \(\dfrac{n}{k}\) 则必然有解。否则深度如果小于等于 \(\dfrac{n}{k}\),则至少有 \(k\) 个叶子。对于一个叶子,因为度数大于等于 \(3\),所以至少有 \(2\) 条返祖边,分别记作 \(p_1, p_2\),其中 \(dep_{p_1} < dep_{p_2}\),那么仅仅由 \(u, p_2\) 构成的环,\(u, p_1\) 构成的环,三者共同构成的环;分析一下发现至少会存在一个不为 \(3\) 的倍数。
深度为 \(w\) 的树至少有 \(\dfrac{n}{w}\) 个叶子,这是最重要的观察!!!
CF1364D Ehab's Last Corollary
仍然是考虑 dfs 树,如果存在一个返祖边使得环大小不超过 \(k\) 那么直接输出。否则每一个返祖边形成的环都超过了 \(k\),间隔选点即可。
CF1325F Ehab's Last Theorem
哥们 Ehab 这是统一了 dfs 树世界是吗。
零 \(p = \lceil\sqrt{n}\rceil\)
构造 dfs 树,如果存在超过 \(p\) 的环就退出,否则我们发现所有环大小均不超过 \(p\)。每次优先选择深度最大的点,将它加入独立集,然后给它的 \(1, 2, \dots, p-1\) 级祖先打上标记,不可以加入独立集。因为可能连边连到。因为一次最多只会新标记 \((p - 1)\) 个节点,所以一共会加入超过 \(p\) 次的新点。
总结:感觉这类问题需要脑电波(比如 CF1103C 那么猎奇的条件一看就是对着答案出性质说是),同时也需要大胆猜测(比如 CF1325F 的染色和 CF1364D 的黑白染色就不同)
3. 双连通相关
3.1 边双联通
割边:如果图中删除此边,使得图的连通分量数量增加那么就被称作一个割边。
从中也可以自然推导出这样的性质:
如果一个图没有割边,那么它就是一个边双连通图。证明非常容易,因为如果在一张没有割边的图上,\(u, v\) 是 \(1\) 边联通的,说明删除期中一条边 \(u, v\) 不连通,连通分量数量增加。
顺着这个性质,我们猜测这样一条性质:如果删除一张图所有的割边,那么每个联通分量内的点两两双连通。证明也是类似的。于是我们可以定义边双连通分量为一个极大的边双联通子图,一张图可以被唯一划分出来成若干边双联通分量。
3.1.1 割边求解
我们从 dfs 树的角度考虑,那么可以得到这样的性质:
- 返祖边不可能成为割边。
- 割边一定为树边,删除割边后剩下的连通分量是它的子树和其补集。
如果一个树边是割边,那么说明不存在一条非树边,使得构成的环包含这条树边,这显然是充要的,于是我们可以得到第一个求解割边的算法:对于非树边 \(x, y\) 那么将树上路径的每个边都权值 \(+1\),用树上差分维护,如果一个树边没有被任何非树边“覆盖”那么它就是割边。这在刻画割边(尤其是一些带点 ds 题里面)很常用。
更常用的是使用 tarjan 算法。tarjan 算法在维护 \(dfn\) 作为 dfs 树的 dfs 序,同时维护 \(low_u\) 为至多经过一条返祖边能到达的最小 \(dfn\)。如果对于父子节点 \(u, v\) 有 \(low_v > dfn_u\) 则显然有 \((u, v)\) 为割边。\(low\) 的求解则是:如果 \((u, v)\) 是父子边,那么 \(low_u = \min\{low_v\}\)。如果是返祖边那么 \(low_u = \min\{low_u, dfn_v\}\)。
3.1.2 边双缩点
边双缩点会直接形成一颗树。边双内部最常用的性质是 \(u, v\) 边双联通的的定义:一个边双连通分量中任意两点之间均有有两条边不交路径。另一种等价描述是:删除任意一条边后任意两点仍然可达。
边双缩点的树最常用的性质则是:对于 \(u, v\) 两点之间路径必经边,就是边双缩点后 \(u\) 所在边双到 \(v\) 所在边双经过的割边。
例题待补充。即将补充一道之前做到的模拟赛题。
3.2 点双联通与圆方树。
割点:如果图中删除此点,使得图的连通分量数增加,那么被称作一个割点。
从中可以自然推导出这样的性质:
如果一个图没有割点,那么它就是一个点双连通图,删除所有割点形成的联通分量内部的确任意两点点双连通,但是我们如果抽出一部分子图,割点也可能会成为非割点。
于是我们从边的角度考虑。点双连通等价于两点之间拥有至少一个简单环。如果两个简单环有公共部分,那么取出公共部分会形成一个新的简单环(后面会题记环空间内容),于是我们可以得到:称两条边同存在一个环中是一个等价关系。根据此等价关系将边划分,那么每一个子图显然都是点双连通的。
3.2.1 割点和点双求解
割点的判定条件:
- 如果 \(u\) 是根,那么 \(u\) 拥有至少两个儿子。
- 如果是非根节点,那么 \(low_v \ge dfn_u\)。
这是显然的,证明从略。
求解点双连通分量就是开一个栈记录一下。重点在于如何刻画点双的形态。
3.2.2 圆方树
刻画点双缩点最好的手段是圆方树。方点代表一个点双,方点向点双内所有圆点连边,构成一颗圆方树。
圆方树最常用的性质是:对于 \(u\) 到 \(v\),其中必经点为圆方树上 \(u\) 到 \(v\) 经过的所有圆点。其它性质如割点就是度数大于 \(1\) 的点其实很自然。
部分题目:
P8456「SWTR-8」地地铁铁
显然和必经点有关,建立点双。只经过一类点比较方便刻画,所以我们考虑只经过一类点的。
定义黑点双为只经过黑点,白点双为只经过白点,灰点双为点双内部两种颜色都有。以下都说黑点。如果两个黑点之间均是黑点双显然合法,如果存在白点双显然不合法,问题主要在于如果出现灰点双怎么办。
首先考虑一个灰点双内部,\(x, y\) 之间的判定。根据点双的性质,对于同一点双内的 \(x, y\) 和边 \(e\) 则必然有 \(x, y\) 的路径经过 \(e\)。手玩容易发现这种情况只会发生在:
- 点双是一个杏仁。
- 杏仁每条链都是同色的
的杏仁的两个端点中。
统计部分是比较容易的。怎么做都行。
P7353 [2020-2021 集训队作业] Tom & Jerry
注意到 J 不能经过 T 这个点非常点双连通,用园方树来刻画结构。
将园方树中以 \(a\) 所在圆点为根,则 \(b\) 只能在它所在的 \(a\) 的子树 \(b'\) 中自由活动。每次 \(a\) 都会尽可能往子树内部堵一下,但是也不一定能够直接走到 \(b'\) 子树内部,有可能原图中没有这条边,导致本来在割点,现在空出,然后 \(b\) 又可以跑到别的地方。由此可见从 T 角度出发来考虑比较麻烦,从 J 角度考虑。
J 必胜的情况有:
-
只涉及一个点双。很显然,此时 J 获胜当且仅当强连通分量中不存在一个点 \(x\) 使得 \(x\) 可以到其中任何一个点。
-
涉及多个点双,反复横跳。那么显然不如直接在两个点双里面横跳。
对于点双 \(A, B\),此时 T 在逼近,记 \(A\) 到 \(B\) 在园方树路径上,\(A\) 出去必经割点 \(u\),到达 \(B\) 必须经过割点 \(v\)。如果 T 已经在 \(A\) 内,不是在割点处那么就可以直接跑到 \(B\),如果在割点处,那么 J 只能暂时跑到一个割点不能直接到达的位置。接下来 T 必然选择移动(如果不动的话 J 不动那么 T 就输了),就会空出割点,于是 \(A\) 还是可以跑到 \(B\)。
综上可以发现,\(A\) 能和 \(B\) 横跳的充要条件为:对于上述定义割点 \(u, v\),满足 \(A, B\) 中均至少存在一个点,到割点 \(u, v\) 的距离大于 \(1\)。
在实现上,若 \(a\) 一开始不在割点,J 那么可以到任意一个点双,否则第一个点双只能在 \(b'\) 这个子树内,但是因为动一步后 \(a\) 能够空出来这个割点,所以另一个点双可以在整棵树上任取。
如何实现?称第一类情况的点双为“好的”。
建立园方树,以一个非割点作为根,接下来做换根 dp,\(f[u]\) 为以 \(u\) 为根的子树中的信息。一个是这个子树内“好的”点双数量,一个是没有被父亲圆点卡住的边双数量,合并起来很容易。然后是 \(g[u]\),用类似的手法做。
我代码好像有点问题。
P9167 [省选联考 2023] 城市建造
神仙题呀
首先刻画判定条件,删除的点集 \(V\) 应当满足为连通块,否则会出现一个连通块有多个关键点。接下来删除这些点之间的边之后这些集合互相不可达,考虑建立园方树刻画。对于 \(x, y\) 若选中则要求它园方树上路径中所有必经点也被选中,又因为同一个点双内点能两两两到达,所示以实际上覆盖的是 \(x\) 到 \(y\) 所有方点相连的所有点。问题实际上转化为删除一个方点连通块,剩下的联通块大小差不超过 \(k\) 的方案数。
对于 \(k\) 的情况,通过手玩可以联想到,对于一个连通块大小 \(d\) 最多只有一种合法解,尝试证明:
引理:\(k = 0\) 下,园方树上中圆点权为 \(1\),方点权为 \(0\),那么最终方案必然会割方点中的带权重心。
直观感受上是显然的,为了使得最终的联通块尽可能平衡,所以割去重心,剩下几个比较平衡的联通块。理性证明似乎也非常容易。假设不割去割点,因为割去的方点联通,所以割去的方点完全只在一个联通块内,那么最终一定会剩下一个大小超过 \(\dfrac{n}{2}\) 的联通块,显然无解。于是得证。这样一个简单的引理为接下来的操作提供了相当之大的便利。
对于 \(k = 0\) 的情况,以重心方点为根,那么如果 \(u\) 要删除 \(u\) 的所有祖先也需要删除。进行讨论,设 \(siz_u\) 为以 \(u\) 为根的子树原点数量,如果 \(siz_u \ge d\) 那么必然要切一刀(等于也要,因为对于根方点显然不存在 \(siz_u = d\),对于其它方点则 \(siz_u = d\) 还会上面有连接的原点,最后大小还是会超过 \(d\))。对于最底层的 \(u\) 显然必然要这么切,那么所有祖先都要切,祖先也满足 \(siz_u \ge d\),所以这样是没问题的。所以如果 \(siz_u < d\) 那么显然不能切。然后我们就知道每个方点要不要切,直接 dfs 一遍判定即可。
对于 \(k = 1\) 的情况,我们类似的先考虑带权重心,但是这个时候我们发现带权重心失效了,可能会出现有多个重心方点的情况,但是我们发现这个时候会产生一个重心圆点,重心方点都是它的儿子,然后用重心原点而不是方点即可。这个“重心方点都是它的儿子”正确性我不知道对不对,因为这是我画图画的。但是如果重心方点不唯一,以更平衡的重心原点为跟肯定没问题。正确性易证,类似上面一眼。此时仍然满足对于方点 \(u\) 如果割去,那么所有 \(u\) 的祖先都是要割去的性质。然后我们继续利用这一点来做。
考虑树形 dp,所有联通块大小在 \([d, d+1]\) 内设 \(f[u]\) 为切割使 \(u\) 子树内合法的方案数,考虑转移。分圆点和方点讨论:
- \(u\) 为圆点
-
对于儿子 \(v\) 有 \(siz_v < d\),显然 \(v\) 不能够删除。
-
对于儿子 \(v\) 有 \(siz_v > d\),类似于上面的分析,此时即便 \(siz_v = d + 1\) 如果不删除连带上面的联通块最终大小都会超过 \(d+1\),\(v\) 要么然是第一次删除,要么然是子树内已经有被删除过了,所以 \(v\) 必然被删除。我们定义一个 \(p = 1\) 不断计算这种情况下 \(p\gets p \times f[v]\)。
-
对于儿子 \(v\) 有 \(siz_v = d\)。这种情况下我们可以考虑将它和 \(u\) 放到一起构成 \((d+1)\),也可以考虑让它内部自己分配。特别的 \(f[v] = 0\) 那么只能和 \(u\) 放到一起。
-
令 \(r\) 为 \(u\) 最少要和多少的联通块在一起,开始 \(r = 1\),对于第一种情况使 \(r\gets r + siz_v\)。特别的对于第三种情况 \(f[v] = 0\) 也纳入 \(r\) 的计算。
接下来如果 \(r = d + 1\),就只能单独为联通块,对于第三种点让 \(p \gets p \times f[v]\),最后 \(f[u] = p\)。如果 \(r = d\) 也可以用上面的转移,如果 \(r = 1\) 也可以选择和下面的 \(v\) 拼接。如果出现第三类点且 \(f[v] = 0\),那么只能和它拼。出现多个 \(f[v] = 0\) 则无解,不出现那么可以考虑选择一个拼的对象,令 \(f[v] \not= 0\) 的第三类点有 \(c\) 个,因为 \(siz_v = d\) 所以 \(f[v]\) 显然只能等于 \(1\) 或 \(0\),在这个情况下就是 \(f[v]c\) 种方案。
- \(u\) 为方点
放到圆点里面讨论,\(f[u] = \prod\limits f[v]\)。
这部分代码:
void dp(int u, int fa, int d) {
if(err) return ;
if(u <= n && siz[u] < d) return ;
for(int i = 0; i < tree[u].size(); i++) {
int v = tree[u][i];
if(v == fa) continue;
dp(v, u, d);
}
if(u <= n) {//圆点
int rest = 1, cnt = 0, mul = 1;
for(int i = 0; i < tree[u].size(); i++) {
int v = tree[u][i];
if(v == fa) continue;
if(siz[v] < d) rest += siz[v];
else if(siz[v] > d)
mul = 1ll * mul * f[v] % Mod;
else {
cnt += (f[v] > 0);
if(!f[v]) rest += d;
}
}
if(rest > d + 1 || rest < d) f[u] = 0;
else f[u] = mul;
if(rest <= 1)
upd(f[u], 1ll * mul * cnt % Mod);
}
else {
f[u] = 1;
for(int i = 0; i < tree[u].size(); i++) {
int v = tree[u][i];
if(v == fa) continue;
f[u] = 1ll * f[u] * f[v] % Mod;
if(!f[u]) break;
}
}
if(siz[u] > d + 1 && !f[u]) {//为 0 减枝
err = 1;
return ;
}
}
\(d\) 看上去有 \(O(n)\) 种,但是实际上因为每个联通块只有可能是 \(n/k\) 的上取整或者下取整,所以也只有 \(O(\sqrt{n})\) 种。要减去一些重复计算的情况。
P8331 [ZJOI2022] 简单题
这个九条可怜怎么这么坏啊/fn
观察一下这个图的性质。任意两个简单环的权相等,利用下面的环空间理论,任意两个环异或后仍然是环,于是对于两个环的情况应该构成一个三条链的杏仁,三条链上的边权相等。如果接下来拼一条也是杏仁从源点出发到汇点的点,那么只要边权相等就合法,否则容易证明不合法。于是这里一个点双是一个杏仁。
回到园方树上,对于 \(u\) 到 \(v\) 的园方树的路径上,每两个相邻圆点之间计算点双内的边权和,最后全部相乘即可。\(w\) 为正整数所以不存在 \(0\) 的情况,总是存在逆元,只要预处理从根开始到每个原点的边权乘积即可。
问题转化为:求点双内部两点的简单路径边权和。
对于 \(u,v\) 分类讨论:
- \(u = S, v = T\),则为点双内所有边的和。
- \(u, v\) 中恰好存在一个等于 \(S\) 或 \(T\)。
- 令 \(u = S, v\not= T\)。倒过来同理。
- 第一种是 \(S\) 直接到 \(v\)。
- 第二种是 \(S\) 经过一条完整路径后走到 \(v\)。
- 令“杏仁”内部有 \(c\) 条纹路,\(v\) 到 \(T\) 的边权和为 \(w\),总边权和为 \(s\),那么有总和为 \(s+(c-2)w\)。
- \(u, v\) 均不为 \(S\),\(T\):
- 同时过 \(S\) 点与 \(T\) 点的路径和:\(u-S-\operatorname{path}-T-v\),要求 \(\operatorname{path}\) 不能为过 \(u\) 或过 \(v\) 的路径。如果 \(u, v\) 不在同一条路径上还可能是 \(u-T-\operatorname{path}-S-v\)。
- 只通过 \(S\):\(u-S-v\)。要求 \(u, v\) 不在同一条路径上,这样的路径是唯一的。
- 只通过 \(T\):\(u-T-v\)。要求 \(u, v\) 不在同一条路径上,这样的路径是唯一的。
- 同时不通过 \(S, T\):\(u-v\),要求 \(u, v\) 在同一条路径上。
- 综合一下,如果 \(u, v\) 在同一条路径,令 \(\operatorname{dist}(S, u) < \operatorname{dist}(S, v)\),那么总和为 \((c-2)(\operatorname{dist}(S, u) + \operatorname{dist}(v, T))+S\)。
- 如果不在同一条路径上,那么就是 \(2s - (c-3)(\operatorname{dist(path_u)} + \operatorname{dist(path_v)})\)。
写起来非常赤石。
3.3 耳分解
Tip:本部分完全不保证严谨性,正确性,如果你觉得我说的很奇怪,欢迎评论区拷打,如果有错误烦请指出,我一定会改正的 qaq
耳分解的本质是对于双连通性的另一种描述。
对于 \(G = (V, E)\) 的一个子图 \(G' = (V', E')\),如果存在一条简单环 \(x_1 \to x_2 \to \dots \to x_k\),满足 \(x_1= x_k \in V'\),且 \(\forall i \in (1, k), x_i \not\in V'\),则称这是闭耳。如果是一个路径,\(x_1 \not= x_k \in V'\) 且 \(\forall i \in (1, k), x_i \not\in V'\),则称这是开耳。
耳分解:
定义:对于一个联通图序列 \(G_0, G_1, \dots, G_k\),如果满足
- \(G_0\) 是一个环,且 \(G_k = G\)。
- \(G_{i - 1}\) 是 \(G_i\) 子图。
- 对于 \(G_i - G_{i - 1}\) 是一个关于 \(G_{i - 1}\) 的开耳或者闭耳。
那么这就称为是图的耳分解。对于开耳分解,要求第三点中只能是开耳,不能是闭耳。我们分别研究这两种分解的性质,注意到他们是非常类似的。
耳分解性质:
- 对于 \(\forall i \in [0, k]\),均有 \(G_i\) 是一个边双连通图。
- 证明:归纳法,\(G_0\) 是一个环显然是边双联通图,接下来 \(G_i\) 是在 \(G_{i - 1}\) 的基础上增加一个环或者路径,显然不出现割边,那么也是边双图。特别的,它可能并不是一个点双图,因为增加的闭耳的公共点可能是割点。比如对于
A--C--D
\/ \/
B E
它可以耳分解,但是不能开耳分解。
- 一个图有耳分解的充要条件是它是边双连通图。
- 证明:
- 必要性:显然。
- 充分性:考虑逐步构造
- 对于 \(G\),以 \(1\) 为根构造一棵 dfs 树,那么以 \(1\) 向下任意一个非树边 \(1\to x\) 所构成的环作为 \(G_0\)。
- 接下来,在 \(G_i\) 只保留树边形成的子图中的一个叶子 \(x\)。如果 \(x\) 在原图中子树内的点没有被完全加入分解,则必然存在 \(x\) 的一个祖先 \(y\)(允许 \(y = x\)),使得 \(y\) 往 \(x\) 子树内有连边。很容易证明正确性,不然就不是边双图了。如果 \(y\) 向 \(x\) 子树内的 \(z\) 有返祖边,那么就以 \(y\to z\) 这条返祖边,然后 \(z\) 到 \(x\) 走树边形成的耳(如果是 \(y = x\) 就是闭耳,否则是开耳)加进来构造成 \(G_{i+1}\)。不断重复以上过程直到 \(G_i = G\)。
- 反之,如果找不到这样的 \(x\),说明所有点都被加入了,那么构造也就完成了。
类似的,我们可以证明开耳分解的两个性质:
- 对于 \(\forall i\in[0, k]\),均有 \(G_i\) 为点双连通图。
- 点数大于等于三的有开耳分解的充要条件是它是点双连通图。
P5776 [SNOI2013] Quare
要求求出最小权和子图,使得子图是一个边双联通图。
考虑到用 dfs 树来刻画边双联通图,判定还是很麻烦的,于是不妨从构造出发,用耳分解来进行构造。考虑扩展一个耳的过程:首先取出来一个环,然后不断增广。对于新增加的耳不断向外走,于是我们要记录 \(G_{i - 1}\) 里的点集合 \(S\),当前耳的部分集合 \(T\),当前点在 \(u\) 处。注意到 \(S, T\) 这两个状态都记录会很大,我们尝试合并。再来观察,不记录 \(S+T\) 的原因是因为我们需要让最后的节点在 \(S\) 内而不是 \(T\),于是我们直接把最后的节点记录在状态里面,而不是记录整个 \(S\)。
具体而言,大致的状态可以设 \(f[S, x, y]\) 为 \(S\) 内的是耳目前考虑过的点,当前耳延伸到 \(u\),最后要走到 \(z\) 的答案,\(g[S]\) 为 \(S\) 内的点在耳内的答案。
考虑转移:考虑从 \(S\) 开始转移,首先为了初始化 \(G_0\) 是环,规定是单点即可。
边双图耳分解可以是闭耳也可以是开耳。但是我们需要特判只经过两条重边就回来的情况。这种情况直接用 \(g[S]\) 往外转移一步即可。
接下来的情况我们强制往外走一条边,同时不能走到出发的那个点。即对于 \(S\) 枚举 \(x\in S\) 作为起点,考虑 \(x\) 能够走到 \(u\),然后 \(u\) 出发,最终走到 \(z\)。
如果 \(x = z\),因为不能立刻从 \(u\) 回到 \(x\),那么我们就强制要求 \(u\) 当前“还在”耳分解里面,最后的时候要求 \(f[S, x, y]\) 中的最后一条边有 \(x\not \in S\) 即可。具体而言如果 \(x = z\),那么用 \(g[S] + e(x, u)\) 更新 \(f[S + \{u\}, u, z]\),否则更新 \(f[S, u, z]\)。然后行走的转移很简单,最后统计 \(x\) 到 \(y\) 最后一条边的时候要求 \(x\not= S\) 即可。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 12;
const int inf = 2e6 + 3;
void upd(int &x, int y) {
x = min(x, y);
}
int g[(1 << N) + 3][N + 3][N + 3];
int f[(1 << N) + 3];
int g1[N + 3][N + 3], g2[N + 3][N + 3];
int n, m;
void init() {
cin >> n >> m;
for(int i = 0; i <= n; i++)
for(int j = 0; j <= n; j++)
g1[i][j] = g2[i][j] = inf;
for(int S = 0; S < (1 << n); S++) {
f[S] = inf;
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
g[S][i][j] = inf;
}
for(int i = 1, x, y, z; i <= m; i++) {
cin >> x >> y >> z; x--, y--;
if(z <= g1[x][y]) {
g2[x][y] = g2[y][x] = g1[x][y];
g1[x][y] = g1[y][x] = z;
}
else g2[x][y] = g2[y][x] = min(g2[x][y], z);
}
for(int i = 0; i < n; i++) f[(1 << i)] = 0;
for(int S = 0; S < (1 << n); S++) {
for(int x = 0; x < n; x++)
for(int y = 0; y < n; y++)
if(((S >> x) & 1) && (!((S >> y) & 1)))
upd(f[S | (1 << y)], f[S] + g1[x][y] + g2[x][y]);
for(int x = 0; x < n; x++) {
if((S >> x) & 1) continue;
for(int y = 0; y < n; y++) {
if(!((S >> y) & 1)) continue;
for(int z = 0; z < n; z++) {
if(!((S >> z) & 1)) continue;
if(y != z) upd(g[S][x][z], f[S] + g1[y][x]);
else upd(g[S | (1 << x)][x][z], f[S] + g1[y][x]);
}
}
}
for(int x = 0; x < n; x++) {
for(int y = 0; y < n; y++) {
if((S >> y) & 1) continue;
for(int z = 0; z < n; z++) {
if(!((S >> z) & 1)) continue;
if(x != y && y != z && x != z)
upd(g[S | (1 << x)][y][z], g[S][x][z] + g1[y][x]);
}
}
}
for(int x = 0; x < n; x++) {
if((S >> x) & 1) continue;
for(int y = 0; y < n; y++) {
if(!((S >> y) & 1)) continue;
upd(f[S | (1 << x)], g[S][x][y] + g1[x][y]);
}
}
}
if(f[(1 << n) - 1] >= inf) cout << "impossible\n";
else cout << f[(1 << n) - 1] << '\n';
}
int main() {
int T; cin >> T;
while(T--) init();
}
3.4 双极定向
据说有很大用处?我不太懂,我来抄写一下 ix35 的论文。
给定一个无向联通图 \(G = (V, E)\) 以及两个点 \(s, t(s \not = t)\),那么以下四个命题等价:
- 添加无向边 \((s, t)\) 后,\(G\) 点双连通。
- \(G\) 的圆方树中的方点提出来构成一条链,\(s\to t\) 是它的一条直径。
- 存在一种对 \(G\) 定向的方法,使得 \(G\) 变成 DAG 且有且仅有 \(s\) 入度为 \(0\),有且仅有 \(t\) 出度为 \(0\)。
- 存在一种点排列方式 \(p_1, p_2, \dots, p_n\) 使得 \(p\) 的每个前缀和后缀均是原图的一个联通导出子图,且 \(p_1 = s, p_n = t\)。
首先证明前两个命题等价,然后证明后两个命题等价,最后证明这两组等价。
对于前两个命题,2 推 1 显然成立,因为 \(s, t\) 这条边和其它边联系起来都进入了同一个环内。然后 1 推 2 也类似,首先建立出圆方树,如果方点形态不是链,并且连接的不是两个直径圆点,那么这条边不会和所有的边合并到同一个环里面去。总的来说是比较显然的。
现在考虑三和四的推导。也比较容易,对于 3 推 4,任何一张入度为 0 的 DAG 正向拓扑过去肯定前缀都是一个联通块,由于只有一个点出度为 0,然后反过来看也是一样的。对于 4 推 3 只要按照顺序定向即可。
考虑用 1,2 推 3。结合一下两者的思想,如果存在一个点双连通图中任意两点 \(s, t\) 均可以以 3 方式定向,那么多个这样的点双以命题 2 形式拼起来成为一条链就可以得到合法方式了。然后考虑一个点双的定向。用开耳分解,首先取出一组带有 \(s, t\) 的环,定向方法唯一,然后每次从环上面找到一个开耳,开耳连边顺序按照环上的顺序即可。本质上说是构造一组很像杏仁但不是杏仁的东西。这样定向会发现只有 \(s,t\) 两个点在“杏仁”的两端,所以有且仅有这两个点入度出度为 \(0\)。
现在考虑 4 推 1。直接从定义出发,如果删除 \(p_x\),那么 \(p_1\sim p_{x-1}\) 联通,\(p_{x+1}\sim p_n\) 联通,又因为 \(p_1 = s, p_n = t\),连接 \(s, t\) 相当于图仍然联通。
于是我们成功证明了四个条件等价。
P9394,我还没写。据说去年省选 d1t3 也是双机定向?
4. 割集,割空间,环空间,割边等价
此前一直在研究割一条边/点的情况,现在考虑多条边。
4.1 割集定义与割空间
对于割集。
定义:将原图 \(G = (V, E)\) 中 \(V\) 划分成 \(V_1, V_2\),则割集 \(\{(u, v)|u\in V_1, v\in V_2\}\)。记作 \(C(V_1, V_2)\)。
一些性质:
- 对于一棵树,任意边集合 \(E_t\) 均为割集,且对应的 \(V\) 划分方案唯一。
- 均为割集很好证明。对于 \(V\) 划分方案,删除这些边后将剩下的联通块缩成一个点,显然一个联通块内均在一个 \(V\) 里面。因为割集最终把联通块连接成一颗树,任意两个缩点后树上均不在同一个 \(V\) 里面,于是按照层数奇偶性就可以唯一划分。
- 对于一张图 \(G = (V, E)\),对于其中一颗生成树边集合 \(E_t\),取出 \(E_t' \subseteq E_t\),那么对于 \(G\) 的割集有且仅有一个割集 \(E_s\) 有 \(E_s \cap E_t' = E_t'\),且划分方式 \(V_1, V_2\) 唯一。
- 这是显然的。因为划分方式 \(V_1, V_2\) 显然唯一,所以最后 \(V_1, V_2\) 原图割集显然唯一就是 \(E_s\)。
- 虽然这条性质相当显然,但是在分析 \(E_s\) 里的非树边时会得到一些有趣的结果:如果 \(e = (x, y) \in E_s\) 是非树边,那么在树边上,根据第一个性质,\(x,y\) 之间应该经过了奇数条 \(E_t'\) 中的边。这位接下来的割空间做好了铺垫。
对于割空间。
将任意一个割集看作是一个 \(m = |E|\) 维度的 01 向量,那么最终的割集实际上是一个异或的线性空间。即任意两个割集的“异或”(只保留出现一次的边)仍然是割集,在异或这个线性运算下,这个空间封闭。
证明:有了上面的性质就不难证明,构造一个生成树 \(E_t\),对于两个割集 \(E_1, E_2\),先考虑树边在考虑对应的非树边。树边上保留下来的割集就是 \((E_1\cap E_t)\oplus (E_2 \cap E_t)\),考虑非树边保留情况,如果非树边 \(e = (x, y)\) 中 \(x\) 到 \(y\) 在 \(E_1, E_2\) 中树边经过总和为奇数就在割集里面,这就等价于 \((x, y)\) 分别在 \(E_1, E_2\) 中经过的奇偶性异或。即非树边的异或。
性质:对于 \(G\) 若有 \(c\) 个联通块,那么割空间的秩为 \(n - c\)。
证明也很容易:只删除一个树边后我们可以得到对应的割集。然后删除任意树边都可以用只删一条边线性表示。树边数量就是 \((n - c)\)。少了任意一条便都表示不出来。于是得证。
4.2 环空间
环控间和割空间有千丝万缕的联系。不过论直接应用,环空间应该会更加广泛。
定义:对于 \(G\) 的导出子图 \(G'\),每个点度数为偶数的导出子图边集看作 \(m\) 维向量,在异或运算下构成线性空间。
证明考虑每次删除一个连通分量欧拉回路。两条欧拉回路如果同时经过一条边,那么删除这条边就合并成一个回路了,接下来只需要按照原来的回路跑就行。删除掉一条欧拉回路对点度数奇偶性不影响,所以这是对的。
由此我们也可以看出来,环空间本质上就是对于每个环(欧拉回路)不断异或产生的空间。它的维数显然就是非树边数量,证明也类似。即 \(m- n+c\)。
4.3 环空间和割空间的联系
环空间和割空间成正交补。
证明:对于 \(G\) 构造一个生成森林,对于树边 \((a, b)\) 和非树边 \((c, d)\),如果 \((c, d)\) 所成环不包含 \((a, b)\),那么显然无交。如果包含 \((a, b)\),那么对应的交集也只有 \((a, b), (c, d)\),最后 \(v_1 \cdot v_2 = 0\)。又因为两者秩之和为 \(m\),所以是正交补。
4.4 切边等价
4.4.1 割集判定
如何判定割集?
一个我自己遇到的定义不明的困惑:割集不等于“删除后使得图不联通的边集”,必须要注意注意到割集的定义:对于一个点集划分 \(V_1, V_2\),那么 \(E = \{(x, y)| x\in V_1, y\in V_2\}\),也就是说 \(V_1\) 和 \(V_2\) 内部的连边不会被割掉。而对于“删除后使得图不联通的边集”则一定包含一个割集,这是显然的。
所以说判定一个严格的割集合可以这么做:
找一个生成树,对于非树边随机赋值,树边则赋一个初始的值。对于一个边集合里面的边权如果异或和为 \(0\) 那么就是割集。这是显然的。
如果要判断删除一个边集是否会使得图不连通,用线性基判断。注意因为异或哈希的问题(如果要保证准确率一般都会记录另外一个维度,比如有多少边。但是因为这里就是要运用异或的特性,所以不能这么记录)。这个做法不能应对边太多的情况,大概 \(15\) 左右。
直接应用是 P10778
我们用上面的方法产生的边权,相等关系可以构成一个等价类,这样的等价类就是切边等价。
4.4.2 切边等价
以下三条结论等价:
- \(e_1, e_2\) 切边等价
- \(e_1, e_2\) 同在一个边双内,同时删除则边双不连通
- 对于一个环,要么然同时包含 \(e_1,e_2\),要么然同时不包含。
虽然 ix35 老师文章里面写比较简略,但是我还是觉得 1 和 2 的讨论很必要
考虑 1 推 2,首先对于图找一个生成树 \(E_t\),每条边的向量按照上面方法生成。如果两条边都是非树边那么必然不切边等价;如果 \(e_1\) 是树边,\(e_2\) 是非树边,那么就是说只有 \(e_2\) 这条边覆盖了 \(e_1\),显然此时这两个边都不是割边,并且一定形成了一个环结构(根据定义),所以这在同一边双。对于同时是树边这个就显然成立。同时删除根据上面割集判定理论显然会导致所在的图不连通,所以边双不连通。
2 推 1 比较简单,边双内两条边同时删除使得边双不连通,因为是一个边双(而不是割边),所以不会有空集,那么这两个边形成的向量一定相同,这符合割边等价的定义。
注意 1 和 2 等价这一点的重要性,根据原来的定义,割边等价可能因为 \(E_t\) 的选择不同而导致产生的等价类不同。根据定义 2,那么无论选择怎样的 \(E_t\),等价类都完全一致。同时这也启发我们在考虑切边等价的时候,考虑好对于割边,对于非树边和树边不同的问题。从逻辑上的推导证明很容易,但是这样的讨论的思考是要做的。反正我在做 Tours 这题,因为没有深刻了解这一点被 elbow 了,呜呜呜
对于 2 推 3,反证,如果有一个环包含 \(e_1\) 不包含 \(e_2\),那么同时删除这两个环都不会变成两个联通分量,则不会产生边双不连通。对于 3 推 2 也是一个道理。
4.4 部分题目
QOJ5089. 环覆盖
可环覆盖等价于每个点度数为偶数。
首先考虑判定,判定是一个图度数均为偶数即合法。
一条边的状态记作 \(b\),那么答案的 gf 为 \(G = \prod\limits_{i = 1}^m(1+x^{b})\)。因为存在一个数量限制,所以我们考虑增加一个 \(y\) 维度维护这件事情,\(G = \prod\limits_{i = 1}^m (1 + x^b y)\)。
以 \(x\) 为主元,每一位上都是关于 \(y\) 的普通多项式。对于一个 \(i\) 的 FWT,用类似于 Tripple 那题的手法,我们很容易数出来 \(\operatorname{FWT}_S(G) = (1+y)^a(1 - y)^{m - a}\) 中的 \(a\) 的数量。接着我们要逆变换求出实际的 \(G_0\),于是:
枚举 \(S\) 之和 \(a_S\) 有关,记录 \(w_x\) 为 \(a_S = x\) 的 \(S\) 数量,然后就可以做到最后 \(O(m^3)\) 统计了。前面处理 \(a_S\) 因为每一条边的状态中集合数量为 \(2\),所以可以 \(O(2^n)\) 求出来,那么最终就是 \(O(2^n + m^3)\) 的。
我严肃怀疑题解中 \(O(2^n + m^2)\) 是笔误,翻了几个最优解似乎都是 \(O(m^3)\) 的。不过也有可能是我二项式恒等式水平太拉垮了 QAQ 如果有朋友能够确定的话能不能教教我 QAQ
P6914 [ICPC 2015 WF] Tours
我还是没有搞懂,欸,晚上再来看吧。
接下来是有向图部分
1. 有向图可达性
求解任意两点之间的可达性经典的可以利用 floyd 求解,不过直接在反图上 bfs 可以做到 \(O(nm)\)。
更加快的做法是缩点后在 DAG 上建反图求解,利用 bitset 记录,是 \(O(\dfrac{nm}{w})\)。与之配套的往往是:操作分块,\(B\) 个一块后一个块内关键点数量就是 \(O(B)\) 个,从而加快计算。
还有一种有奇效的方法:利用线段树代替 bitset,维护一个异或哈希。如果两侧完全相同那么就不继续递归下去,建立可持久化线段树合并。随即情况下这个东西跑得飞快。
QOJ4000 弥留之国的爱丽丝
洛谷我被卡常了,这我没办法。
操作分块,\(B\) 个为一块。对于一个块内的操作只涉及到 \(2B\) 个关键点(包括边的两个端点和查询的两个点),显然我们只关心这些点之间可达性即可。首先做一遍 kosaraju 缩点求出不考虑所有被删除和修改的边的可达性,这部分是 \(O(\dfrac{nB}{w})\) 的,修改直接修改,查询暴力 bfs,这部分是 \(O(B^2)\) 的。于是最终复杂度是 \(O(\dfrac{n^2}{w} + nB)\)。
我洛谷卡不过去。
*QOJ4420. Range Reachability Query
必须考虑增量加边后点的可达性。我不太好说清楚这个题目的动机,好牛啊 TAT
设 \(f[u, i]\) 为对于 \((l_i, r_i, v_i)\) 的询问,能否有 \(u\) 走到 \(v\)。转移考虑增量不断加入边 \(x\to y\),那么 \(f[x, i] \to f[x, i] | f[y, i]\)。将最后一维用 bitset 刻画,令 \(S_c\) 为满足 \(l_i \le c \le r_i\) 的集合 \(c\),那么 \(f[x] \to f[x] | (f[y] \& S_c)\)
2. 强连通性
2.1 tarjan 算法
tarjan 算法仍然从 dfs 树出发。对于有向图 dfs 树有树边,非树边分为三类,前向边,返祖边,横叉边。前向边显然没有用处,返祖边非常有用,可以直接构成一个环,横插边如果能够走到反向边那么就有用,否则没有用。
仍然维护 \(dfn, low\),\(low\) 代表经过最多一条反向边,\(u\) 和字数内部可以走到的最小 \(dfn\)。如果 \(dfn_u = low_u\) 说明是一个强连通分量。但是发现它的横叉边可能会走到一个之前已经形成强连通分量的地方。考虑到用 \(dfn_u = low_u\) 判断强连通分量,则这一定是以 \(u\) 为根子树里面一段连通块,所以我们用一个栈维护当前遍历过的还没有形成强连通分量的点,\(low\) 修改定义:经过子树内最多经过一条边能到达的栈内节点中 dfn 最小点。求强连通分量直接弹出来就可以了。
我不是很喜欢 tarjan 求解强连通分量。可能是因为 dfs 树没有那么优美的性质了。
2.2 Kosaraju 算法
这位才是重量级啊/chongbai
首先建立反图,在反图回溯的时候记录次序。然后根据这个次序倒着走直接搜索,最后得到了强连通分量的划分。核心在于我们建立反图回溯时记录次序,本质上是记录了缩点后各个强连通分量的拓扑序,所以我们按照这个倒着的拓扑序,在正图上遍历就是对的。这一条性质常常可以简化缩点后 dp 的代码,提高效率。在 2-SAT 问题中很好用。
提供一份代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4, M = 1e5;
vector <int> vec[N + 10];
vector <int> gra[N + 10], ngr[N + 10];
bool vis[N + 10];
int col[N + 10], cl = 0, que[N + 10], tot = 0;
void dfs1(int u) {
vis[u] = 1;
for(int i = 0; i < ngr[u].size(); i++) {
int v = ngr[u][i];
if(vis[v]) continue;
dfs1(v);
}
que[++tot] = u;
}
void dfs2(int u) {
vec[cl].push_back(u);
col[u] = cl;
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(col[v]) continue;
dfs2(v);
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, m; cin >> n >> m;
for(int i = 1, x, y; i <= m; i++)
cin >> x >> y,
gra[x].push_back(y),
ngr[y].push_back(x);
for(int i = 1; i <= n; i++)
if(!vis[i]) dfs1(i);
for(int i = tot; i >= 1; i--) {
int u = que[i];
if(!col[u]) {
++cl;
dfs2(u);
}
}
cout << cl << '\n';
for(int u = 1; u <= n; u++) {
if(vec[col[u]].empty()) continue;
sort(vec[col[u]].begin(), vec[col[u]].end());
for(int i = 0; i < vec[col[u]].size(); i++)
cout << vec[col[u]][i] << ' ';
cout << '\n';
vec[col[u]].clear();
}
}
2.3 耳分解
有向图中对于 \(G = (V, E)\),耳定义为:对于一段路径 \(x_1, x_2, \dots, x_k\),如果 \(x_1, x_k \in G\),但是 \(\forall i \in [2, k), x_i \not \in G\),那么称这个路径是关于 \(G\) 的一个耳。
而分解这样定义:对于一串图序列:\(G_0, G_1, \dots, G_k\)。
- \(G_0\) 是一个环,允许为 \(0\),\(G_k = G\)
- \(G_i - G_{i - 1}\) 构成一个耳。
- \(G_{i- 1}\) 是 \(G_i\) 的导出子图。
仍然类似于无向图的耳分解,有如下结论:
一张图可以耳分解的充要条件为这张图是一张强连通图。
证明:必要性显然。考虑充分性。对于一张强连通图,以 \(1\) 为根向下走,那么除了 \(1\) 点应该都有 \(low_u < dfn_u\)。即 \(u\) 在子树内能找到一个点 \(y\) 使得 \(y\) 可以走到一个 dfn 序比 \(u\) 更小的 \(v\) 处。考虑按照 dfn 序从小到大加入每个点,对于每个新加入的 \(u\) 找到它的 \(f_u \to u \to y \to z\) 作为一个耳。加入完所有点后,任意一条边就都是耳了。即可构造出耳分解。
一个对称的题目是 QOJ3301。做法几乎一致,除了这里不允许重边。要注意到定向可以变成修改方向啊!
2.4 无向图定向
无向图 \(G\) 可以被定向为强连通图,当且仅当 \(G\) 是边双连通图。
证明:
充分性显然,随便找一个生成树,树边构造成外向树,非树边从后代到祖先。如果两点是祖先后代关系,那么显然可以到达。如果并非祖先后代关系,那么可以先到 LCA 再到达。于是很容易发现这是强连通图。
现在考虑证明必要性。对于一张强连通图构造其耳分解,归纳法,然后容易发现不定向情况下不存在割边。
2.5 部分题目
uoj134. 【UR #9】App 管理器
观察 1:
我们很容易想到这样的必要条件:
- 将所有无向边拆成两个有向边,为强连通图。
- 将所有有向边看作是无向边,为边双连通图。
这个必要性是显然的。而且非常强(因为两个子问题都是充要条件),我们有理由考虑它就是原问题的充要条件。
观察 2:
对于无向边只有 \((u, v)\) 的情况,当且仅当删除 \((u, v)\) 后,\(u, v\) 互相不可达时无解。否则如果能够从 \(u\) 到 \(v\),就定向为 \(v\) 到 \(u\),反之亦然。
我们考虑利用观察 1 的必要条件来对观察 2 的做约束,然后这样不断给无向边定向。定向后观察 1 的第二点不受影响,第一点合法,这样做归纳。于是问题转化为对于观察 1 中条件下观察 2 的问题是否满足即可。
考虑反证法,如果成立,那么删除 \((u, v)\) 这条边,\(u\) 能到的点集为 \(S\),\(v\) 能到的点集为 \(T\),因为 \(u, v\) 互相不可达,所以 \(S, T\) 是对全集的一个划分。如果 \(S, T\) 内部没有连边,那么如果把有向边看作无向边这条 \((u, v)\) 就是一个割边,那么不合法;否则一定有从 \(S\) 到 \(T\) 或者从 \(T\) 到 \(S\) 的边,于是可以得到 \(u, v\) 相互得到的关系,假设不成立。于是得证。
我们从而可以证明这个必要条件的充分性,第一个提供归纳的基础,第二个提供归纳的条件。
3. 有向图环结构
3.1 可环覆盖子图
类似于无向图的环空间,有向图的可环覆盖子图定义为可以用若干简单环覆盖的子图,等价于对于所有点入度等于出度的子图,可以用欧拉回路证明。
没有像无向图那样“两个环异或仍然是环”的优美性质,有向图中的性质主要结合于 xor 或者 \(\bmod m\) 运算下。以论文中 \(\bmod m\) 为例。
3.2 在 \(\bmod m\) 下的性质
性质 1:对于一张强连通图,如果 \(x\) 到 \(y\) 存在路径长度为 \(S\),那么必然存在一条 \(y\) 到 \(x\) 的路径长度 \(T\) 满足 \(T\equiv -S \pmod m\)
证明比较容易。\(m = 1\) 显然成立,否则令存在一条 \(y\) 到 \(x\) 的路径长度为 \(T'\),那么可以通过重复 \(m\) 遍构造一个 \(y \to x\) 的长度为 \(mT' + (m - 1)S\) 的路径,在模 \(m\) 意义下和 \(-S\) 同余。
这启发我们可以对于 \(w(x, y)\) 这样一条有向边,构造一条 \(-w(y, x)\) 的反向边,边权取反,此时这个图的可环覆盖结构中所能生成的 \(\bmod m\) 意义下集合等价。我们把一个有向图就转化为了类似于一个无向图的问题。
类似的,为了研究这类环上问题,我们构造一棵生成树 \(E_t\),特别的这里我们认为树边 \((x, y)\) 的反向边 \((y, x)\) 也是树边。发现只经过树边,那么从 \(x\) 到 \(y\) 权值就是 \(dep_y - dep_x\)。
性质 2:以每一条非树边(包括反向边的非树边),令其边权为 \(z_i\),构造出一组基 \(\{z_i + dep_{y_i} - dep_{x_i}\}\),那么最后每一个可环覆盖结构边权和必然为这样一个基线性生成。
ix35 老师的证明看上去很怪怪啊
证明:首先考虑如何刻画一组可环覆盖结构。每次拿出一组欧拉回路这样下去。欧拉回路总是可以看作是若干个点不交的有向环拼起来,这很容易归纳法,在一个回路路径中每次截取第一次出现重复的段落是一个环,然后发现剩下的仍然入度等于出度。
于是我们只考虑一个简单环,对于这种情况,那么就是树边和非树边混合,先走下行,再走上行,对于一次上行或者下行覆盖的树边不交。同时因为是简单环,所以要求上行下行中树边和非树边互补,否则就不是简单环了。
然后就很明显了,因为上下行树边非树边互补,所以我们构造出来的肯定是这个基的线性组合。
这里面主要是:将可环覆盖子图->拆成欧拉回路->拆成简单环。欸所以 ix35 老师证明有没有问题啊 TAT
性质 3:对于所有可环覆盖子图,边权的 \(\gcd\) 为上面提出的一组基(就是对于每个非树边求一下)的 \(\gcd\)。
因为显然答案上界是这个,然后因为线性组合以后它的 \(\gcd\) 还是不变,然后就结束了。
在 xor 或者是不进位加法看作对于每一位都满足上述性质即可。
3.3 部分题目
CF1515G Phoenix and Odometers
直接的应用。只考虑 \(v\) 所在连通分量内的环,那么产生的环一定是 \(d\) 的倍数。因为你环可以随便走,所以你构造出一组以后可以不断重复。令这是 \(kd \equiv b \pmod m\),即判定的充要条件为 \(\gcd(d, m)|b\)。
QOJ5402. 术树数
口胡,没有写代码。
现在不完全是 \(x\) 和 \(y\) 的可环覆盖子图,而是 \(x\) 到 \(y\) 的路径。注意这是无向图。
我们考虑可覆盖环空间这件事情,从 \(x\) 点开始走树边,走到一个环,然后回来。注意到,树边可以反复走很多遍,但是最后一定是 \(2w\) 的倍数。于是我们可以将这个路径看作是从 \(x\) 到 \(y\) 的路径和若干个环(包括上面说一条无向边构成的环)的并。
于是以 \(x\) 到 \(y\) 路径为初值,愉快的利用 k 进制线性基查询。

浙公网安备 33010602011771号