Компоненты вершинной двусвязности
Дан неориентированный граф без петель в следующем формате:
- Первая строка -
n
m
(|V|, |E| соответственно) - Следующие m строк содержат описание ребер:
i
u_i
v_i
.
Требуется выделить компоненты вершинной двусвязности в нем в след. формате:
- Первая строка - количество компонент вершинной двусвязности графа
- Вторая строка -
m
чисел, которые говорят, к какой компоненте связности относитсяi
-ое ребро
Тест:
Вход:
5 6
1 2
2 3
3 1
1 4
4 5
5 1
Выход:
2
1 1 1 2 2 2
Так вот, вроде бы теория в целом правильная, но на тестах скрытых падает (тест выше код мой проходит)... Теорию черпал из след. источников:
- https://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BC%D0%BF%D0%BE%D0%BD%D0%B5%D0%BD%D1%82_%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D0%BD%D0%BE%D0%B9_%D0%B4%D0%B2%D1%83%D1%81%D0%B2%D1%8F%D0%B7%D0%BD%D0%BE%D1%81%D1%82%D0%B8
- https://neerc.ifmo.ru/wiki/index.php?title=%D0%98%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4%D0%B0_%D0%B2_%D0%B3%D0%BB%D1%83%D0%B1%D0%B8%D0%BD%D1%83_%D0%B4%D0%BB%D1%8F_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0_%D1%82%D0%BE%D1%87%D0%B5%D0%BA_%D1%81%D0%BE%D1%87%D0%BB%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F
Возможно, проблема в том, что я как-то неправильно работаю с ребрами, но вроде кратность я учитываю. Вот сам код:
#include <iostream>
#include <vector>
#include <map>
#include <set>
#define fast_cin() std::ios_base::sync_with_stdio(false); std::cin.tie(NULL); std::cout.tie(NULL)
int n, m, T;
std::vector<std::vector<int>> G;
std::vector<bool> used, visited, is_multiple_edge;
std::vector<int> best_up, tin;
std::set<int> art_points;
std::map<int, int> colors;
std::map<std::pair<int, int>, int> edges_ind;
int max_color;
std::pair<int, int> Edge(int a, int b) {
if (a > b) std::swap(a, b);
return {a, b};
}
void add_edge(int a, int b, int ind) {
G[a].push_back(b);
G[b].push_back(a);
if (!edges_ind[Edge(a, b)]) edges_ind[Edge(a, b)] = ind;
else is_multiple_edge[edges_ind[Edge(a, b)]] = true;
}
void dfs(int v, int p = -1) {// Помимо текущей вершины, добавим прямого предка (изначально считаем, что его нет)
T++;
best_up[v] = tin[v] = T;
used[v] = true;
int children = 0;
for (auto u: G[v]) {
if (u == p) continue;
if (used[u]) {
best_up[v] = std::min(best_up[v], tin[u]);
} else {
dfs(u, v);
children++;
best_up[v] = std::min(best_up[v], best_up[u]);
if (p != -1 && best_up[u] >= tin[v]) { // Условие точки сочленения, когда v - не корень
art_points.insert(v);
}
}
}
if (p == -1 && children > 1) {
// Если v корень и количество его сыновей в дереве поиска больше 1, то вершина v является точкой сочленения
// Если корень с детьми удалить, то его поддеревья станут несвязными между собой
art_points.insert(v);
}
}
void paint(int v, int color, int parent = -1) {
visited[v] = true;
for (auto u: G[v]) {
if (u == parent) continue;
if (!visited[u]) {
if (best_up[u] >= tin[v] && !is_multiple_edge[edges_ind[Edge(v, u)]]) {
max_color++;
colors[edges_ind[Edge(v, u)]] = max_color;
paint(u, max_color, v);
} else {
colors[edges_ind[Edge(v, u)]] = color;
paint(u, color, v);
}
} else if (tin[u] < tin[v] && !is_multiple_edge[edges_ind[Edge(v, u)]]) {
colors[edges_ind[Edge(v, u)]] = color;
}
}
}
void find_art_points() {
T = 1;
for (int i = 1; i <= n; i++) {
if (!used[i]) dfs(i);
}
}
void solve() {
find_art_points();
max_color = 0;
for (int i = 1; i <= n; i++) {
if (!visited[i]) {
paint(i, max_color);
}
}
}
int main() {
fast_cin();
std::cin >> n >> m;
G.resize(n + 1);
used.resize(n + 1);
best_up.resize(n + 1);
tin.resize(n + 1);
visited.resize(n + 1);
is_multiple_edge.resize(m + 1);
for (int i = 1; i <= m; i++) {
int a, b;
std::cin >> a >> b;
add_edge(a, b, i);
}
solve();
std::cout << max_color << "\n";
for (auto [_, comp]: colors) {
std::cout << comp << " ";
}
return 0;
}
Заранее спасибо.
Ответы (1 шт):
Короче, я создал монстра...
Начал хранить индексы ребер в виде мапы: [Ребро] : [Множество индексов ребер, кратных ребру из ключа]
; затем, когда я хотел красить одно ребро из этого множества, красил их всех, т.к. по факту это одно ребро. Наверняка можно красивее, но давайте я оставлю так:
#include <iostream>
#include <vector>
#include <map>
#include <set>
#define fast_cin() std::ios_base::sync_with_stdio(false); std::cin.tie(NULL); std::cout.tie(NULL)
int n, m, T;
std::vector<std::vector<int>> G;
std::vector<bool> used, visited;
std::vector<int> best_up, tin, colors;
std::set<int> art_points;
std::map<std::pair<int, int>, std::vector<int>> edges_ind;
int max_color;
std::pair<int, int> Edge(int a, int b) {
if (a > b) std::swap(a, b);
return {a, b};
}
void add_edge(int a, int b, int ind) {
G[a].push_back(b);
G[b].push_back(a);
edges_ind[Edge(a, b)].push_back(ind);
}
void dfs(int v, int p = -1) {// Помимо текущей вершины, добавим прямого предка (изначально считаем, что его нет)
T++;
best_up[v] = tin[v] = T;
used[v] = true;
int children = 0;
for (auto u: G[v]) {
if (u == p) continue;
if (used[u]) {
best_up[v] = std::min(best_up[v], tin[u]);
} else {
dfs(u, v);
children++;
best_up[v] = std::min(best_up[v], best_up[u]);
if (p != -1 && best_up[u] >= tin[v]) { // Условие точки сочленения, когда v - не корень
art_points.insert(v);
}
}
}
if (p == -1 && children > 1) {
// Если v корень и количество его сыновей в дереве поиска больше 1, то вершина v является точкой сочленения
// Если корень с детьми удалить, то его поддеревья станут несвязными между собой
art_points.insert(v);
}
}
void paint(int v, int color, int parent = -1) {
visited[v] = true;
for (auto u: G[v]) {
if (u == parent) continue;
if (!visited[u]) {
if (best_up[u] >= tin[v]) {
max_color++;
for (auto ind: edges_ind[Edge(v, u)]) {
colors[ind] = max_color;
}
paint(u, max_color, v);
} else {
for (auto ind: edges_ind[Edge(v, u)]) {
colors[ind] = color;
}
paint(u, color, v);
}
} else if (tin[u] < tin[v]) {
for (auto ind: edges_ind[Edge(v, u)]) {
colors[ind] = color;
}
}
}
}
void find_art_points() {
T = 1;
for (int i = 1; i <= n; i++) {
if (!used[i]) dfs(i);
}
}
void solve() {
find_art_points();
max_color = 0;
for (int i = 1; i <= n; i++) {
if (!visited[i]) {
paint(i, max_color);
}
}
}
int main() {
fast_cin();
std::cin >> n >> m;
G.resize(n + 1);
used.resize(n + 1);
best_up.resize(n + 1);
tin.resize(n + 1);
visited.resize(n + 1);
colors.resize(m + 1);
for (int i = 1; i <= m; i++) {
int a, b;
std::cin >> a >> b;
add_edge(a, b, i);
}
solve();
std::cout << max_color << "\n";
for (int i = 1; i < colors.size(); i++) {
std::cout << colors[i] << " ";
}
return 0;
}