接上文,说一下如何求点双.
思路:
要注意一点的是:点双连通分量之间是有公共点的(而且一定是割点)。所以,栈需要记录生成树上的边,而不是点。
改动点:
1.当 d f n [ v ] = = 0 dfn[v] == 0 dfn[v]==0时,边入栈.
2.当 d f n [ u ] ≤ l o w [ v ] , v ∈ u 的 后 继 时 dfn[u] \leq low[v],v \in u的后继时 dfn[u]≤low[v],v∈u的后继时,代表着 u u u连接着的 v v v的这颗子树无法不通过父节点回到更早的时候,即 u ∪ 子 树 v u\cup 子树v u∪子树v构成了一个点双。此时,栈顶存着这个连通分量中的所有边.以此弹出染色即可。
3.千万注意:单个点也是点双.所以做题的时候要考虑图是否连通。
模板题:T103492
题目大意:
给你一张无向图,查询所有点双.
题目思路:
没说连通,那么要特判单个点的情况.
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e4 + 5;
const int maxm = 6e5 + 5;
int u[maxm] , v[maxm] , nextt[maxm] ,first[maxn] , sign;
void addedge(int x , int y){u[++sign]=x;v[sign]=y;nextt[sign]=first[x];first[x]=sign;}
int n , m;
int dfn[maxn] , low[maxn] , cnt , tot;
struct Edge {int x , y;};
vector<int> res[maxn];
stack<Edge> s;
void tarjan (int g , int fa)
{
dfn[g] = low[g] = ++cnt;
if (first[g] == 0) {
res[++tot].push_back(g);
}
for (int i = first[g] ; i ; i = nextt[i]){
if (!dfn[v[i]]){
s.push({g , v[i]});
tarjan(v[i] , g);
if (dfn[g] <= low[v[i]]){
++tot;
while (1){
Edge now = s.top();s.pop();
res[tot].push_back(now.y);
if (now.x == g) {
res[tot].push_back(g);
break;
}
}
}
low[g] = min (low[g] , low[v[i]]);
}else
low[g] = min (low[g] , dfn[v[i]]);
}
}
int main()
{
ios::sync_with_stdio(false);
cin >> n >> m;
for (int i = 1 ; i <= m ; i++){
int x , y; cin >> x >> y;
addedge(x , y);
addedge(y , x);
}
for (int i = 1 ; i <= n ; i++){
if (!dfn[i]) tarjan(i , 0);
}
for (int i = 1 ; i <= tot ; i++){
for (auto g : res[i]) cout << g << " ";
cout << endl;
}
return 0;
}