tarjan模板总结

Tarjan

模板

void tarjan(int u,int fa)
{
	//最初能到达的dfn最小的点为其本身
	dfn[u]=low[u]=++tdfn;
	
	for(int i=tail[u];i;i=e[i].pre)
	{
		int v=e[i].v;
		//如果这个点还没到达,就走一走
		if(!dfn[v])
		{
			tarjan(v,i);
			//v可以到达的dfn最小的点,u也可以到达
			low[u]=min(low[u],low[v]);
			//如果v可以到达的dfn最小的点的dfn大于u的dfn
			//即v在树上,再怎么往上走,都只能走到u的下面
			if(dfn[u]<low[v]) bri[i]=bri[i^1]=1;
		}
		//因为可能有重边,所以用fa特判不是走的下来的路
		//如果这条边到了u及u以上的某个点,则更新一下low
		else if(i!=(fa^1)) low[u]=(low[u],dfn[v]);
	}
}

割边

即删掉此边图会分裂的边,满足bri[i]==1的边都是割边。

void tarjan(int u,int fa)
{
	dfn[u]=low[u]=++tdfn;
	
	for(int i=tail[u];i;i=e[i].pre)
	{
		int v=e[i].v;
		if(!dfn[v])
		{
			tarjan(v,i);
			low[u]=min(low[u],low[v]);
			if(dfn[u]<low[v]) bri[i]=bri[i^1]=1;
		}
		else if(i!=(fa^1)) low[u]=(low[u],dfn[v]);
	}
}

割点

若x不为搜索树根节点,且满足至少有一个v满足dfn[x]<=low[v]。

若x为搜索树根节点,则x至少有两个子节点。

void tarjan(int u)
{
	dfn[u]=low[u]=++tdfn;
	int ff=0;
	for(int i=tail[u];i;i=e[i].pre)
	{
		int v=e[i].v;
		if(!dfn[v])
		{
			tarjan(v,i);
			low[u]=min(low[u],low[v]);
			//如果v再怎么走最多也只能到v
			//即只能靠u与树中u上面的点连接
			//则u为割点
			if(dfn[u]<=low[v])
			{
				ff++;
				//特别的,如果是树根,就要另做判断
				if(u!=root||ff>1) cut[u]=1;
			}
		}
		else low[x]=min(low[x],dfn[y]);
	}
}

无向图

边双连通分量

void tarjan(int u,int fa)
{
	dfn[u]=low[u]=++tdfn;
	
	for(int i=tail[u];i;i=e[i].pre)
	{
		int v=e[i].v;
		if(!dfn[v])
		{
			tarjan(v,i);
			low[u]=min(low[u],low[v]);
			if(dfn[u]<low[v]) bri[i]=bri[i^1]=1;
		}
		else if(i!=(fa^1)) low[u]=(low[u],dfn[v]);
	}
}
//额外增加一个c数组记录每个点属于哪个双连通分量
void dfs(int u)
{
	c[u]=tc;
	
	for(int i=tail[u];i;i=e[i].pre)
		if(!c[v]&&!bri[i]) dfs(e[i].v);
}

边双联通的缩点

void tarjan(int u,int fa)
{
	dfn[u]=low[u]=++tdfn;
	
	for(int i=tail[u];i;i=e[i].pre)
	{
		int v=e[i].v;
		if(!dfn[v])
		{
			tarjan(v,i);
			low[u]=min(low[u],low[v]);
			if(dfn[u]<low[v])
            {
            	bri[i]=bri[i^1]=1;
            	//加缩点后的边,将双联通看作一个点,桥为边
            	add_(u,v);
            }
		}
		else if(i!=(fa^1)) low[u]=(low[u],dfn[v]);
	}
}
void dfs(int u)
{
	c[u]=tc;
	
	for(int i=tail[u];i;i=e[i].pre)
		if(!c[v]&&!bri[i]) dfs(e[i].v);
}

点双联通分量

void tarjan(int u)
{
	dfn[u]=low[u]=++tdfn;
	//加入栈
	q[++q[0]]=u;
    
	//孤立点单独成一个联通块
	if(x==root||tail[u]==0)
	{
		dcc[++td].push_back(u);
		return;
	}
	
	//常规操作
	int ff=0;
	for(int i=tail[u];i;i=e[i].pre)
	{
		int v=e[i].v;
		if(!dfn[v])
		{
			tarjan(v,i);
			low[u]=min(low[u],low[v]);
			if(dfn[u]<=low[v])
			{
				ff++;
				if(u!=root||ff>1) cut[u]=1;
				
				//新建一个双联通
                //取出栈中所有的点
				td++;
				int z=q[q[0]--];
				while(z!=v)
				{
					dcc[td].push_back(z);
					z=q[q[0]--];
				}
				dcc[cnt].push_back(u);
			}
		}
		else low[x]=min(low[x],dfn[y]);
	}
}

点双联通的缩点

	num=td;
	//将所有的割点看作一个单独的点
	for(int i=1;i<=n;++i)
	if(cut[i]) new_id=++num;
	tc=1;
	//所有的点除开割点后作为一个单独的点
	for(int i=1;i<=td;++i)
	for(int j=0;j<dcc[i].size();++j)
	{
		int x=dcc[i][j];
		if(cut[x])
		{
			//加边
			add_c(i,new_id[x]);
			add_c(new_id[x],i);
		}
		else c[x]=i;
	}
posted @ 2020-10-23 20:35  林生。  阅读(162)  评论(0)    收藏  举报