点双连通分量

\(\text{luogu-8435}\)

对于一个 \(n\) 个节点 \(m\) 条无向边的图,求其点双连通分量的个数,并且输出每个点双连通分量。

\(1 \le n \le 5 \times10 ^5\)\(1 \le m \le 2 \times 10^6\)


以下题解部分来自于 强连通分量 | 点双连通分量 | 边双连通分量 - 知乎

在一张连通的无向图中,对于任意两个点 \(u\)\(v\) ,任意去掉一个这张图中的其中一个点(除了 \(u\)\(v\)),\(u\)\(v\) 的连通性都没有发生改变,那么 \(u\)\(v\) 就是点双连通的。对于一张无向图,其点双连通的极大子图,被称为点双连通分量

对于求解具体的点双连通分量,我们可以直接在判断割点的时候求解点双连通分量。具体来讲,我们只需要额外加一个栈,记住访问的子树,之后遇到判断割点的条件成立的时候说明有点双连通分量,直接像下面代码一样放入即可。

注意:如果这个点是孤立点的话,需要特判,它自己就是一个点双连通分量。

#include<iostream>
#include<cstdio>
#include<vector>
#include<stack>
using namespace std;
#define MAXN 500005

long long read() {
	long long x = 0, f = 1;
	char c = getchar();
	while(c > 57 || c < 48) { if(c == 45) f = -1; c = getchar(); }
	while(c >= 48 && c <= 57) { x = (x << 1) + (x << 3) + (c - 48); c = getchar(); }
	return x * f;
}

long long n, m, dfn[MAXN], low[MAXN], dn, cnt;
vector<vector<long long> > scc;
vector<long long> v[MAXN];
stack<long long> st;
bool f[MAXN];

void tarjan(long long x, bool fp) {
	dfn[x] = low[x] = ++ dn;
	long long son = 0; st.push(x);
	if(v[x].empty() && fp) { scc.push_back({x}); return; }
	for(auto y : v[x]) 
		if(!dfn[y]) {
			tarjan(y, 0), low[x] = min(low[x], low[y]);
			if(low[y] >= dfn[x]) {
				son ++;
				if(!fp || son >= 2) f[x] = 1;
				long long t = 0;
				scc.push_back({});
				do {
					t = st.top(), st.pop(), scc.back().push_back(t);
				} while(t != y);
				scc.back().push_back(x);
			}
		}
		else low[x] = min(low[x], dfn[y]);
	return;
}

int main() {
	n = read(), m = read();
	for(int i = 1; i <= m; i ++) {
		long long x = read(), y = read();
		if(x == y) continue;
		v[x].push_back(y), v[y].push_back(x); 
	}
	for(int i = 1; i <= n; i ++) if(!dfn[i]) tarjan(i, 1);
	cout << scc.size() << "\n";
	for(auto it : scc) {
		cout << it.size() << " ";
		for(auto x : it) cout << x << " ";
		cout << "\n";
	}
	return 0;
}

\(\text{luogu-B3610}\)

在离散数学课程的学习中,大家学习了无向图中”割点“和“块”的定义,现在来检查一下大家的学习情况。

给定一张 \(n\) 个点 \(m\) 条边的无向图,点的编号从 \(1\)\(n\) ,可能存在重边和自环,不保证是一张连通图。现在,请你求出这张图所有的块。

注意,我们不把一个没有任何与其相连边的点看成割点;因此,一个单独的点构成的连通块不被看成是块。你可以通过样例理解这个事情。

在输出的时候,我们规定这么一个输出的顺序:首先,对于一个块,我们把该块中所有点按照编号从小到大排序;然后,对于两个块,我们规定,把点按照顺序拿出来排成一个序列,字典序较小的排在前面。这样,我们就可以对所有块规定了一个顺序。最终输出就按照这样的顺序输出。

\(1 \le n \le 5 \times 10^4\)\(1 \le m \le 3 \times 10^5\)


实际上就是点双连通分量去除孤立点。

还需要处理一下输出的排序,按下面这种方式排序即可。

for(auto &it : scc) sort(it.begin(), it.end());
sort(scc.begin(), scc.end());
posted @ 2026-01-13 18:15  So_noSlack  阅读(0)  评论(0)    收藏  举报