Skip to content

Commit b6a1e55

Browse files
committed
Euler trail detection
1 parent 889f4a5 commit b6a1e55

File tree

4 files changed

+154
-16
lines changed

4 files changed

+154
-16
lines changed

cp-algo/graph/base.hpp

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,17 @@ namespace cp_algo::graph {
2525
concept edge_type = std::is_base_of_v<edge_base, edge_info>;
2626

2727
enum type {directed = 0, undirected = 1};
28-
template<type undirected, edge_type edge_info = edge_base>
28+
template<type _undirected, edge_type edge_info = edge_base>
2929
struct graph {
30-
graph(int n, int v0 = 0): _n(n), m(0), v0(v0), adj(n) {}
30+
static constexpr bool undirected = _undirected;
31+
graph(int n, int v0 = 0): _n(n), _m(0), v0(v0), _adj(n) {}
3132

3233
void add_edge(int u, edge_info e) {
33-
m++;
34-
adj.push(u, size(edges));
34+
_m++;
35+
_adj.push(u, size(edges));
3536
edges.push_back(e);
3637
if constexpr (undirected) {
37-
adj.push(e.to, size(edges));
38+
_adj.push(e.to, size(edges));
3839
}
3940
edges.push_back(e.backedge(u));
4041
}
@@ -44,26 +45,30 @@ namespace cp_algo::graph {
4445
add_edge(u, e);
4546
}
4647
}
47-
void call_adjacent(int v, auto &&callback, auto &&terminate = [](){return false;}) const {
48-
for(int sv = adj.head[v]; sv && !terminate(); sv = adj.next[sv]) {
49-
callback(adj.data[sv]);
48+
void call_adjacent(int v, auto &&callback, auto &&terminate) const {
49+
for(int sv = _adj.head[v]; sv && !terminate(); sv = _adj.next[sv]) {
50+
callback(_adj.data[sv]);
5051
}
5152
}
52-
int deg(int v) const {
53-
int ans = 0;
54-
call_adjacent([&ans](){ans++;});
55-
return ans;
53+
void call_adjacent(int v, auto &&callback) const {
54+
call_adjacent(v, callback, [](){return false;});
55+
}
56+
void call_edges(auto &&callback) const {
57+
for(int v: nodes_view()) {
58+
call_adjacent(v, [&](int e) {callback(v, e);});
59+
}
5660
}
5761
auto nodes_view() const {
5862
return std::views::iota(0, _n);
5963
}
60-
edge_info& edge(int e) {return edges[e];}
61-
edge_info const& edge(int e) const {return edges[e];}
64+
auto const& incidence_lists() const {return _adj;}
65+
auto const& edge(int e) const {return edges[e];}
6266
int n() const {return _n;}
67+
int m() const {return _m;}
6368
private:
64-
int _n, m, v0;
69+
int _n, _m, v0;
6570
std::vector<edge_info> edges;
66-
data_structures::stack_union<int> adj;
71+
data_structures::stack_union<int> _adj;
6772
};
6873
}
6974
#endif // CP_ALGO_GRAPH_BASE_HPP

cp-algo/graph/euler.hpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#ifndef CP_ALGO_GRAPH_EULER_HPP
2+
#define CP_ALGO_GRAPH_EULER_HPP
3+
#include "base.hpp"
4+
#include <algorithm>
5+
#include <utility>
6+
#include <vector>
7+
namespace cp_algo::graph {
8+
template<typename graph>
9+
int euler_start(graph const& g) {
10+
std::vector<int> deg(g.n());
11+
constexpr bool undirected = graph::undirected;
12+
int res = 0;
13+
g.call_edges([&](int u, int e) {
14+
res = u;
15+
if constexpr (undirected) {
16+
deg[u] ^= 1;
17+
} else {
18+
deg[u]++;
19+
deg[g.edge(e).to]--;
20+
}
21+
});
22+
auto nodes = g.nodes_view();
23+
auto is_start = [&](int v) {return deg[v] > 0;};
24+
auto starts = std::ranges::count_if(nodes, is_start);
25+
if(starts > 1 + undirected) {
26+
res = -1;
27+
} else if(starts == 1 + undirected) {
28+
auto start = *std::ranges::find_if(nodes, is_start);
29+
res = deg[start] == 1 ? start : -1;
30+
}
31+
return res;
32+
}
33+
auto euler_trail(auto const& g) {
34+
int v0 = euler_start(g);
35+
std::vector<int> trail;
36+
if(~v0) {
37+
std::vector<bool> used(g.m());
38+
auto& adj = g.incidence_lists();
39+
auto head = adj.head;
40+
auto dfs = [&](auto &&self, int v) -> void {
41+
while(head[v]) {
42+
int e = adj.data[std::exchange(head[v], adj.next[head[v]])];
43+
if(!used[e / 2]) {
44+
used[e / 2] = 1;
45+
int u = g.edge(e).to;
46+
self(self, u);
47+
trail.push_back(e);
48+
}
49+
}
50+
};
51+
dfs(dfs, v0);
52+
std::ranges::reverse(trail);
53+
}
54+
return std::pair{v0, trail};
55+
}
56+
}
57+
#endif // CP_ALGO_GRAPH_EULER_HPP

verify/graph/euler_directed.test.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// @brief Eulerian Trail (Directed)
2+
#define PROBLEM "https://judge.yosupo.jp/problem/eulerian_trail_directed"
3+
#pragma GCC optimize("Ofast,unroll-loops")
4+
#pragma GCC target("tune=native")
5+
#include "cp-algo/graph/euler.hpp"
6+
#include <bits/stdc++.h>
7+
8+
using namespace std;
9+
using namespace cp_algo::graph;
10+
11+
void solve() {
12+
int n, m;
13+
cin >> n >> m;
14+
graph<directed> g(n);
15+
g.read_edges(m);
16+
auto [v0, es] = euler_trail(g);
17+
if(ssize(es) != m) {
18+
cout << "No" << "\n";
19+
} else {
20+
cout << "Yes" << "\n";
21+
cout << v0 << ' ';
22+
for(auto e: es) {cout << g.edge(e).to << ' ';}
23+
cout << "\n";
24+
for(auto e: es) {cout << e / 2 << ' ';}
25+
cout << "\n";
26+
}
27+
}
28+
29+
signed main() {
30+
//freopen("input.txt", "r", stdin);
31+
ios::sync_with_stdio(0);
32+
cin.tie(0);
33+
int t = 1;
34+
cin >> t;
35+
while(t--) {
36+
solve();
37+
}
38+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// @brief Eulerian Trail (Undirected)
2+
#define PROBLEM "https://judge.yosupo.jp/problem/eulerian_trail_undirected"
3+
#pragma GCC optimize("Ofast,unroll-loops")
4+
#pragma GCC target("tune=native")
5+
#include "cp-algo/graph/euler.hpp"
6+
#include <bits/stdc++.h>
7+
8+
using namespace std;
9+
using namespace cp_algo::graph;
10+
11+
void solve() {
12+
int n, m;
13+
cin >> n >> m;
14+
graph<undirected> g(n);
15+
g.read_edges(m);
16+
auto [v0, es] = euler_trail(g);
17+
if(ssize(es) != m) {
18+
cout << "No" << "\n";
19+
} else {
20+
cout << "Yes" << "\n";
21+
cout << v0 << ' ';
22+
for(auto e: es) {cout << g.edge(e).to << ' ';}
23+
cout << "\n";
24+
for(auto e: es) {cout << e / 2 << ' ';}
25+
cout << "\n";
26+
}
27+
}
28+
29+
signed main() {
30+
//freopen("input.txt", "r", stdin);
31+
ios::sync_with_stdio(0);
32+
cin.tie(0);
33+
int t = 1;
34+
cin >> t;
35+
while(t--) {
36+
solve();
37+
}
38+
}

0 commit comments

Comments
 (0)