求双连通分量(tarjan方法)
解释都在代码里:
#include<iostream>
#include<cstdio>
#include<cstring>
#define CLR(a,b) memset(a,b,sizeof(a))
#define Min(a,b) a>b?b:a
using namespace std;
const int INF=1e5+5;
int dfn[INF]; //记录各结点的访问次序
int low[INF]; //low是追溯到DCC的根节点的dfn的值
//当根节点的某个直接儿子节点的low值大于或等于根节点的dfn的值时,
//就可以从栈中取值了,直到取到根节点为止时就是一个DCC
int dcc_id[INF]; //存储各节点的所在的编号(就是你给他们编的号,从1到n)cnt_dcc就是编号下标!
int stack[INF]; //栈:用深搜搜索节点并依次存储各个节点—以便于找到DCC
//即当发现环时就是一个DCC,用low标记的,从该栈中取值取到该根节点为止
int father[INF]; //由于求DCC是在一个无向连通图中,即为双向的图
//该father就是为了防止某一节点又访问上一个节点(上一个节点搜出该节点)
int top,cnt_dcc,cnt_idex;
struct Edge //存储边的信息
{
int to;
Edge *next;
};
Edge *edge[INF];
void tarjan(int u)
{
dfn[u]=low[u]=++cnt_index;
stack[++top]=u;
for(Edge *temp=edge[u];temp;temp=temp->next)
{
int v=temp->to;
if(!dfn[v])
{
father[v]=u;
tarjan(v);
low[u]=Min(low[u],low[v]); //更新low使每一个DCC中的low的值==根节点low值
if(low[v]>=low[u]) //当发现节点u的儿子节点v的low值>=dfn[u]
{ //则就要取栈,即是时候取出DCC了
cnt_dcc++;
int j;
do
{
j=stack[top--];
dcc_id[j]=cnt_dcc;
}while(v!=j);
top++; //这里为什么要++?因为连着DCC是有共同节点的
}
}
else if(v!=father[v])
low[u]=Min(low[u],dfn[v]);
}
}
void solve(int node) //node 为要深搜的结点
{
top=cnt_dcc=cnt_idex=0;
CLR(dfn,0);
tarjan(node); //由于是无向连通图,只需深搜一个节点就都可以都到了
}