Tarjan求强连通分量
在一个有向图中,如果某两点间都有互相到达的路径,那么称中两个点强联通,如果任意两点都强联通,那么称这个图为强联通图;一个有向图的极大强联通子图称为强联通分量。
算法可以在 的时间内求出一个图的所有强联通分量。
表示进入结点 的时间
表示从 所能追溯到的栈中点的最早时间
如果某个点 已经在栈中则更新
否则对 进行回溯,并在回溯后更新
#include<iostream> #include<cstdlib> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<stack> using namespace std; int n,m,tot,ind,ans; int dfn[200005],low[200005],last[200005]; bool ins[200005]; stack<int> s; struct hh { int fr,to,next; }e[500005]; void add(int fr,int to) { e[++tot].to=to;e[tot].fr=fr; e[tot].next=last[fr]; last[fr]=tot; } void tarjan(int now) { int i,j; s.push(now); ins[now]=true; low[now]=dfn[now]=++dex; for(i=last[now];i;i=e[i].next) if(!dfn[e[i].to]) { tarjan(e[i].to); low[now]=min(low[now],low[e[i].to]); } else if(ins[e[i].to])low[now]=min(low[now],dfn[e[i].to]); if(dfn[now]==low[now]) { cnt=0; do { j=s.top();s.pop(); ins[j]=false; cnt++; }while(j!=now); ans=max(ans,cnt); } } int main() { int i,j,u,v; scanf("%d%d",&n,&m); for(i=1;i<=m;i++) { scanf("%d%d",&u,&v); add(u,v); } for(i=1;i<=n;i++) if(!dfn[i]) tarjan(i); printf("%d",ans); return 0; }
Tarjan求割点
在一个无向图中,如果删掉点 后图的连通块数量增加,则称点 为图的割点。
对于搜索树上的非根结点 ,如果存在子节点 满足 ,即 向上无法达到 的祖先,则 为割点。
对于搜索树上的根节点,若它的子节点数 ,则 为割点。
void tarjan(int x,int fa) { int i,j; dfn[x]=low[x]=++dex; for(i=last[x];i;i=e[i].next) { ++t[x]; if(!dfn[e[i].to]) { tarjan(e[i].to,x); low[x]=min(low[x],low[e[i].to]); if(x==root&&t[x]>=2) opt[x]=true; else if(x!=root&&low[e[i].to]>=dfn[x]) opt[x]=true; } else if(e[i].to!=fa) low[x]=min(low[x],dfn[e[i].to]); } }
Tarjan求割边
对于当前结点 ,若邻接点中存在结点 满足 ,则 为割边。
void tarjan(int x,int fa) { int i,j; low[x]=dfn[x]=++dex; for(i=last[x];i;i=e[i].next) if(e[i].to!=fa) { if(dfn[e[i].to]) low[x]=min(low[x],dfn[e[i].to]); else { tarjan(e[i].to,x); low[x]=min(low[x],low[e[i].to]); if(low[e[i].to]>dfn[x]) opt[e[i].id]=true; } } }