欧拉图

欧拉图的判定方法 :

判定无向图是欧拉图:非零度顶点是连通的,顶点的度数都是偶数

判定无向图是半欧拉图:非零度顶点是连通的,恰有 2 个奇度顶点

判定有向图是欧拉图:非零度顶点是强连通的,每个顶点的入度和出度相等

判定有向图是半欧拉图:非零度顶点是弱连通的,至多一个顶点的出度与入度之差为 1,
至多一个顶点的入度与出度之差为 1 吗,其他顶点的入度和出度相等

计算欧拉回路/通路的算法 :

欧拉图 - OI Wiki

Fleury 算法

不常用,暂不介绍。

Hierholzer 算法

也称逐步插入回路法。

以问题为例:

即输出字典序最小的欧拉路径

Q:如何判定是否存在欧拉回路?欧拉通路呢?

(挖坑....)

算法类似模板:

auto dfs = [&](auto&& self, int x) -> void {
	for (int& i = cur[x]; i < m; i++) {
		if (g[x][i]) {
			g[x][i] = g[i][x] = 0;
			self(self, i);
		}
	}
	a.push_back(primes[x]);
	};
dfs(dfs, 0);

目前的应用场景:

1981(div2)

D. Turtle and Multiplication

给一个整数 n(2n106) ,构造一个由满足以下条件的整数组成的数列 a1,a2,,an

  • 对于所有 1in1ai3105
  • 对于所有 1i<jn1 , aiai+1ajaj+1 .

在所有这些序列中,小猪要求乌龟找出具有最少不同元素的序列。

Solution

欧拉路径/Hierholzer 算法

需要学习欧拉图相关知识, 注:这个部分比较冷门

欧拉图问题

要使得 aiai+1ajaj+1,只要 ai 都取素数。这样只要满足 (ai,ai+1),(aj,aj+1) 是不同的即可(即经过不重复的边)。

(ai,ai+1) 看作是图的一条边,则问题变为找到点数最小的无向完全图(加上自环 (ai=ai+1)),使得这个图存在一条经过 (n1) 条边且不经过重复边的路径。

若完全图的点数为 m 。(这时确定了点数,需要使得边数尽可能大)

  • m 为奇数,则每个点的度数都是偶数,则一定存在欧拉路径,路径则为边数 m(m+1)2

  • m 为偶数,则每个点的度数都为奇数,这个时候需要删除一些边至少保证有欧拉通路,最多只能剩下两个奇数度数的点,所以至少要删除 m21 条边,路径长度为 m(m+1)2m2+1=m22+1

若当 n=106 时, m1415,第 m 个素数是 118073×105,满足要求。

所以这时只需要确定最小的 m,再跑一遍欧拉通路即可。

对于最小的 m,满足:
m1(mod2){n1m(m+1)2n1>(m1)m2   m0(mod2){n1m22+1n1>(m1)22+1

代码:

void solve() {
    int n;
    cin >> n;

    int m = 1;
    while (n - 1 > (m % 2 == 1 ? m * (m + 1) / 2 : m * m / 2 + 1)) {
        m++;
    }

    vector<int> a;
    a.reserve(n);

    vector<vector<int>> g(m, vector<int>(m, 1));
    vector<int> cur(m);
    if (m % 2 == 0) {
        for (int i = 1; i < m - 1; i += 2) {
            g[i][i + 1] = g[i + 1][i] = 0;
        }
    }

    auto dfs = [&](auto&& self, int x) -> void {
        for (int& i = cur[x]; i < m; i++) {
            if (g[x][i]) {
                g[x][i] = g[i][x] = 0;
                self(self, i);
            }
        }
        a.push_back(primes[x]);
        };
    dfs(dfs, 0);

    a.resize(n);
    for (int i = 0; i < n; i++) {
        cout << a[i] << " \n"[i == n - 1];
    }
}

这里需要用到筛法;
!模板整理