tarjan相关问题

图的 dfs

如果是无向图,求 dfs 树,需要记录每条边及其反边是否被遍历过(包括树边和非树边),无法简单的记录父亲或父亲边编号来判断。

概述

  1. 有向图都需要栈,无向图都不需要(求点双,圆方树除外)。
  2. 无向图和有向图的差异在 else 里,不在 if 里。

有向图

缩点

注:2-SAT 使用的是有向图缩点

void tarjan(int u){
	dfn[u]=low[u]=++tim;vis[u]=1;sk[++top]=u;
	for(int i=head1[u];i;i=e1[i].next){
		int v=e1[i].to;
		if(!dfn[v]){
			tarjan(v);low[u]=min(low[u],low[v]);
		}
		else if(vis[v]) low[u]=min(low[u],low[v]);
	}
	if(low[u]==dfn[u]){
		tot++;int now=0;
		while(now!=u){
			now=sk[top--];
			vis[now]=0;co[now]=tot;
		}
	}
} 

无向图

1-1点双

void tarjan(int u,int rt){
	if(u==rt&&head[u]==0){// 需要特判孤点
		ans[++tot].push_back(u);return;
	}
	dfn[u]=low[u]=++tim;sk[++top]=u;
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;
		if(!dfn[v]){
			tarjan(v,rt);
			low[u]=min(low[u],low[v]);
			if(low[v]>=dfn[u]){// 其实无需求出割点
				tot++;
				int now=0;
				while(now!=v){
					now=sk[top--];ans[tot].push_back(now);
				}
				ans[tot].push_back(u);
			}
		}
		else low[u]=min(low[u],dfn[v]);
	}
}

1-2圆方树(类似求点双)

int tot=n;//编号>n的点为方点
void tarjan(int u){
	dfn[u]=low[u]=++tim;sk[++tp]=u;
	for(int i=h1[u];i;i=e1[i].next){
		int v=e1[i].to;
		if(!dfn[v]){
			tarjan(v);low[u]=min(low[u],low[v]);
			if(low[v]>=dfn[u]){
				int now=0;tot++;
				while(now!=v){
					now=sk[tp--];add(tot,now,0,e2,h2,l2);add(now,tot,0,e2,h2,l2);
				}
				add(tot,u,0,e2,h2,l2);add(u,tot,0,e2,h2,l2);
			}
		}
		else low[u]=min(low[u],dfn[v]);
	}
}

1-3割点

void tarjan(int u,int ft,int root){
	dfn[u]=low[u]=++tim;
	int ch=0;
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;
		if(!dfn[v]){
			tarjan(v,u,root);
			if(u==root){
				ch++;if(ch>1) res[u]=1;	
			}
			else res[u]=res[u]|(low[v]>=dfn[u]);
			low[u]=min(low[u],low[v]);
		}
		else low[u]=min(low[u],dfn[v]);
	}
}

2-1边双

void tarjan(int u,int ft){
	dfn[u]=low[u]=++tot;
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;
		if(v==ft) continue;// 这句话不可少
		if(!dfn[v]){
			tarjan(v,u);
			low[u]=min(low[u],low[v]);
			if(low[v]>dfn[u]){// 严格大于
				b[i]=b[i^1]=1;
			}
		}
		else{
			low[u]=min(low[u],dfn[v]);
		}
		
	}
}

命名空间法求圆方树

namespace Tarjan{
	int head[N],len;
	E e[N];
	int dfn[N],low[N],tim;
	int sk[N],top;
	void add(int u,int v){
		e[++len]=E{v,head[u]};head[u]=len;
	}
	void clear(){
		for(int i=1;i<=n;i++) head[i]=dfn[i]=0;
		tim=len=top=0;
	}
	void tarjan(int u){
		dfn[u]=low[u]=++tim;sk[++top]=u;
		for(int i=head[u];i;i=e[i].next){
			int v=e[i].to;
			if(!dfn[v]){
				tarjan(v);low[u]=min(low[u],low[v]);
				if(low[v]>=dfn[u]){
					tot++;int now=0;
					::add(u,tot);::add(tot,u);
					while(now!=v){
						now=sk[top--];
						::add(tot,now);::add(now,tot);
					}	
				}
			}
			else low[u]=min(low[u],dfn[v]);
		}
	}
	void solve(){
		tot=n;
		for(int i=1;i<=n;i++){
			if(!dfn[i]) tarjan(i);
		}
	}
}
posted @ 2025-05-09 11:43  born_to_sun  阅读(18)  评论(0)    收藏  举报