点双连通分量
\(\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());
本文来自博客园,作者:So_noSlack,转载请注明原文链接:https://www.cnblogs.com/So-noSlack/p/19478817

浙公网安备 33010602011771号