洛谷P8435 【模板】点双连通分量 题解

题目链接:https://www.luogu.com.cn/problem/P8435

解题思路完全参考自 Jeremiahy 大佬的博客 。特别是文章的最后一部分。

(系统维护,博客点不进,那先直接进 https://www.luogu.com.cn/problem/solution/P8435

注意:有 重边自环!!

示例程序:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e5 + 5;
int n, m, dfn[maxn], low[maxn], idx, dcc, fat[maxn];
bool iscut[maxn];
vector<int> vec[maxn];
struct Node {
	int id, v;
};
vector<Node> g[maxn];
stack<int> stk;

void new_dcc(int u, int x) {
	dcc++;
	int v;
	do {
		v = stk.top();
		stk.pop();
		vec[dcc].push_back(v);
	} while (v != u);
	if (u != x)
		vec[dcc].push_back(x);
}

void tarjan(int u, int fe) {
	dfn[u] = low[u] = ++idx;
	int cnt = 0;
	for (auto e : g[u]) {
		int id = e.id, v = e.v;
		if (!dfn[v]) {
			fat[v] = id;
			cnt++;
			tarjan(v, id);
			low[u] = min(low[u], low[v]);
			if (low[v] >= dfn[u])
				iscut[u] = true;
		}
		else if (id != fe)
			low[u] = min(low[u], dfn[v]);
	}
	if (fe == -1 && cnt >= 2)
		iscut[u] = true;
}

void dfs(int u) {
	if (!fat[u] && g[u].size() == 0) { // 独根树
		vec[++dcc].push_back(u);
		return;
	}
	stk.push(u);
	for (auto e : g[u]) {
		int id = e.id, v = e.v;
		if (fat[v] == id) {
			dfs(v);
			if (low[v] >= dfn[u] && iscut[u]) {
				new_dcc(v, u);
			}
		}
	}
	if (!fat[u]) {	// 根节点
		if (stk.size() > 1)
			new_dcc(u, u);
		while (!stk.empty())
			stk.pop();
	}
}

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; i++) {
		int u, v;
		scanf("%d%d", &u, &v);
		if (u == v) continue; // 过滤掉重边
		g[u].push_back({i, v});
		g[v].push_back({i, u});
	}
	for (int i = 1; i <= n; i++)
		if (!dfn[i])
			tarjan(i, -1), dfs(i);
	printf("%d\n", dcc);
	for (int i = 1; i <= dcc; i++) {
		printf("%d", vec[i].size());
		for (auto x : vec[i])
			printf(" %d", x);
		puts("");
	}
	return 0;
}

posted @ 2024-01-25 01:07  quanjun  阅读(24)  评论(0编辑  收藏  举报