欧拉回路,欧拉路径,欧拉图
定义
对于一个图,如果存在一条路径恰好经过所有边一次,则称这条路径为一条欧拉路径。
如果存在一条回路经过所有边恰好一次,则称这条回路为一条欧拉回路。
存在欧拉回路的图被称为欧拉图。
性质
对于连通图 \(G\),以下三个性质是互相等价的:
- \(G\) 是欧拉图;
- \(G\) 中所有顶点的度数都是偶数(对于有向图,每个顶点的入读等于出度);
- \(G\) 可被分解为若干条不共边回路的并。
下面证明这三哥性质是互相等价的(只证明无向图的情况,有向图的情况类似):
若 \(G\) 是欧拉图,\(G\) 中所有顶点的度数显然都是偶数。
接下来,我们证明 \(2\) 可以推 \(3\):
首先把所有的自环拿出来,保证图中没有自环。
随便选一个点 \(u\),从 \(u\) 开始,找到一个出边 \((u,v)\),走到 \(v\) 后删掉边 \((u,v)\),接着继续遍历。每次删掉边后图中最多只会存在 \(2\) 个点度数为奇数,分别为路径的起点和终点,又因为图中没有重边,所以必须再经过一次起点才能使得图中的点的度数全为偶数,这样就会找到一条回路。删完一条回路之后,图中的每个点的度数仍然为偶数,同样可以用上述方法找到一条回路并删掉,最后一定可以把图删空。显然这些回路没有公共边,证毕。
将有公共点的两条回路可以合并成一条,最后一定可以合并成一条大回路。所以 \(G\) 是欧拉图。
半欧拉图与欧拉图类似,充要条件是恰好出现 \(2\) 个奇度点,分别是起点和终点。如果是有向图,则恰有一个点 \(\text{out}_u-\text{in}_u=1\) 作为起点,恰有一点 \(\text{in}_u-\text{out}_u=1\) 作为终点。证明很简单就不写了。
求法
Hierholzer 算法
跟上面的证明类似,每次找到一个回路然后和之前的回路合并。
但是比较难写,一般不用这种写法。
DFS 写法
从任意一个点 DFS,枚举没有经过的出边,在回溯的时候把路径压入栈中。
只能在回溯的时候压入栈中,不然会出问题,但是最后的栈要翻转一下才是欧拉回路中的每个点。
- 有向图:
void Dfs(int u) {
for (int &i = cur[u], v; i < g[u].size(); i++) {
Dfs(g[u][i][0]);
}
stk[++top] = u;
}
- 无向图:
void Dfs(int u) {
for (int &i = cur[u], v; i < g[u].size(); i++) {
if (vis[g[u][i][0]]) continue;
vis[g[u][i][0]] = 1;
v = g[u][i][1];
Dfs(v);
}
stk[++top] = u;
}
代码里面加入了当前弧优化,不优化复杂度是 \(O(|V||E|)\),优化后复杂度就是 \(O(|V|+|E|)\)。
当然当前弧优化也可以从后往前遍历 std::vector,并用 std::vector 的 pop_back。
如果要求欧拉路径,就找到起点,从起点开始 DFS 即可。
最小字典序欧拉回路 / 欧拉路径:
将 \(u\) 的出边排序即可。
CF527E Data Center Drama
首先找到答案下界,图中的奇度点肯定是需要连边的,考虑所有奇度点两两连边,接着如果边数是奇数那么也需要多连一条边,又因为不能出现奇度点,所以只能连自环。我们下面证明这就是答案。
因为所有点的度数都是偶数所以这张图存在欧拉回路,我们把欧拉回路找出来,相邻两条边的定向不同就可以构造完成。
CF429E Points and Segments
首先考虑所有整点被覆盖次数为偶数怎么做。
把红色看成区间 \(+1\),把蓝色看成区间 \(-1\),那么需要满足每个整点上的数属于 \(\{-1,0,1\}\)。
考虑把序列差分,\([l,r]\) 区间 \(+1\) 相当于差分序列 \(d_l\gets d_l+1\),\(d_{r+1}\gets d_{r+1}-1\),区间 \(-1\) 相当于 \(d_l\gets d_l-1\),\(d_{r+1}\gets d_{r+1}+1\)。
如果我们把前者理解为 \(l\to r+1\) 连一条边,后者理解为 \(r+1\to l\) 连一条边。我们给 \(l\) 到 \(r+1\) 连无向边,显然这个图是欧拉图,我们可以定向让所有点的入度与出度只差为 \(0\),即可以让差分里面所有的数都为 \(0\)。所以可以让所有整点上的数都等于 \(0\)。
如果存在整点被覆盖次数为奇数,如果 \(x\) 的覆盖次数为奇数,我们添加一个区间 \([x,x]\),最后可以将所有的整点覆盖次数都变成偶数,而且最后删掉这些区间的时候也只会让答案修改 \(\pm 1\),绝对值也不会超过 \(1\),所以这么构造答案就是对的。
P7816 「Stoi2029」以父之名
对于点 \(u\),\(u\) 的边权为 \(1\) 的出边数量为奇数。
对于边权 \(1\) 的出边和边权为 \(2\) 的出边数量均为奇数的点 \(u\),我们希望 \(1\) 中间两两配对,\(2\) 中间两两配对,最后留出 \(1,2\) 配对绝对值为 \(1\)。
对于边权为 \(2\) 的出边数量为偶数的点 \(u\),我们希望 \(2\) 中间两两配对,\(1\) 中间两两配对,最后剩下一个 \(1\)。
考虑建立虚点向所有奇度点连长度为 \(1\) 的边,那么就变成了 \(1,2\) 数量均为偶数,我们希望 \(1\) 两两配对,\(2\) 两两配对,绝对值为 \(0\),去点新边绝对值变成 \(1\)。
在这个图中跑欧拉回路并尽量走边权相同的边,直接跑就是对的。
CF1610F Mashtali: a Space Oddysey
CF1610F Mashtali: a Space Oddysey
注意到答案上界为所相连的边权值之和为奇数的点的个数,和上面的题目差不多,不再赘述。
AT_agc018_f [AGC018F] Two Trees
AT_agc018_f [AGC018F] Two Trees
首先需要满足点 \(u\) 在两颗树中儿子的个数奇偶性相同。
儿子个数可以转化为度数,根的话可以建一个虚点连向两个根,那么就不需要多余分讨。
牵扯到度数的奇偶性考虑欧拉回路,对于度数为奇数的点 \(u\),我们考虑连一条边在两棵树的这个点之间,这两个点设为 \(u,u'\)。
在这个图上跑欧拉回路,如果是偶度点,\(X_u\gets 0\)。
如果是奇数度点,边是从左到右就是 \(1\),从右到左就是 \(-1\)。
可以证明这就是符合条件的。
证明大概就是,将欧拉回路拆分成若干个环,考虑每个环对子树的贡献,内部环肯定没有贡献,经过父亲的环会有 \(\pm 1\) 的贡献,但是只会出现一个这样的环。

浙公网安备 33010602011771号