Skip to content

Commit d39e2b2

Browse files
committed
Paths of length 2 decomposition for undirected graph
1 parent ad27b23 commit d39e2b2

File tree

2 files changed

+107
-0
lines changed

2 files changed

+107
-0
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#pragma once
2+
#include "../data_structure/light_forward_list.hpp"
3+
#include "../unionfind/unionfind.hpp"
4+
#include <cassert>
5+
#include <tuple>
6+
#include <utility>
7+
#include <vector>
8+
9+
// 無向グラフを長さ2の道(**閉路を含む**)へ分解
10+
// 各連結成分について,辺の本数が偶数なら完全な分解が可能
11+
// Complexity: O(V + E)
12+
// Verify: - https://codeforces.com/contest/1519/problem/E
13+
// - https://atcoder.jp/contests/agc035/tasks/agc035_b
14+
struct PathsOfLength2Decomposition {
15+
int V, E;
16+
std::vector<std::pair<int, int>> edges;
17+
std::vector<light_forward_list<std::pair<int, int>>> to_nonmst;
18+
std::vector<light_forward_list<std::pair<int, int>>> to_mst;
19+
UnionFind uf;
20+
void _init(int V_) {
21+
V = V_, E = 0;
22+
edges.clear();
23+
to_nonmst.assign(V, {});
24+
to_mst.assign(V, {});
25+
uf = UnionFind(V);
26+
}
27+
PathsOfLength2Decomposition(int V = 0) { _init(V); }
28+
void add_edge(int u, int v) {
29+
assert(u >= 0 and u < V);
30+
assert(v >= 0 and v < V);
31+
if (uf.unite(u, v)) {
32+
to_mst[u].push_front({E, v}), to_mst[v].push_front({E, u});
33+
} else {
34+
to_nonmst[u].push_front({E, v}), to_nonmst[v].push_front({E, u});
35+
}
36+
edges.emplace_back(u, v);
37+
E++;
38+
}
39+
40+
std::vector<std::pair<int, int>> _ret;
41+
std::vector<char> _visited;
42+
std::vector<char> _edge_used;
43+
void _dfs(int now, int prv) {
44+
_visited[now] = 1;
45+
int prveid = -1;
46+
std::vector<int> available_edges;
47+
for (auto ev : to_mst[now]) {
48+
int eid, nxt;
49+
std::tie(eid, nxt) = ev;
50+
if (nxt == prv)
51+
prveid = eid;
52+
else {
53+
_dfs(nxt, now);
54+
if (!_edge_used[eid]) available_edges.push_back(eid);
55+
}
56+
}
57+
for (auto ev : to_nonmst[now]) {
58+
int eid, nxt;
59+
std::tie(eid, nxt) = ev;
60+
if (!_edge_used[eid]) available_edges.push_back(eid);
61+
}
62+
if ((available_edges.size() & 1) and prv != -1) available_edges.push_back(prveid);
63+
for (unsigned h = 0; h + 1 < available_edges.size(); h += 2) {
64+
int e1 = available_edges[h], e2 = available_edges[h + 1];
65+
_edge_used[e1] = _edge_used[e2] = 1;
66+
_ret.emplace_back(e1, e2);
67+
}
68+
}
69+
std::vector<std::pair<int, int>> run() { // 辺番号(0-origin, 追加順)の組の列を返す
70+
_ret.clear();
71+
_visited.assign(V, 0);
72+
_edge_used.assign(E, 0);
73+
for (int i = 0; i < V; i++) {
74+
if (!_visited[i]) _dfs(i, -1);
75+
}
76+
return _ret;
77+
}
78+
};
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
title: 無向グラフの長さ 2 のパスへの分解
3+
documentation_of: ./paths_of_length_two_decomposition.hpp
4+
---
5+
6+
## 使用方法
7+
8+
```cpp
9+
PathsOfLength2Decomposition graph(N);
10+
for (int m = 0; m < M; m++) {
11+
int a, b;
12+
cin >> a >> b;
13+
graph.add_edge(a - 1, b - 1);
14+
}
15+
vector<pair<int, int>> pairs = graph.run();
16+
```
17+
18+
## 背景
19+
20+
- まず全域森を構成する.
21+
- 全域森に使用しなかった辺のみに着目し,各頂点の次数が 1 以下になるまで長さ 2 のパスを作れるだけ作る.
22+
- 最後に,全域森を DFS し,葉の方から各頂点で辺を使い切る.
23+
- この構成より,各連結成分について辺の本数が偶数なら全ての辺を使い切ることが可能(奇数なら DFS の根で辺が一つ余る).
24+
25+
## 問題例
26+
27+
- [AtCoder Grand Contest 035 B - Even Degrees](https://atcoder.jp/contests/agc035/tasks/agc035_b)
28+
- 連結なので,辺の本数が偶数なら解ける.
29+
- [Educational Codeforces Round 108 (Rated for Div. 2) E. Off by One](https://codeforces.com/contest/1519/problem/E)

0 commit comments

Comments
 (0)