关于tarjan算法的一些整理

要NOIP了,来补一些算法。

主要是为了讲一下tarjan的衍生各种定义,以及讲解一下代码之间的联系与不同,想看算法原理的可自行百度。

当然马蜂也都是很久以前的了,不喜勿喷。

强连通分量

定义(显然仅在有向图中有):一个极大子图满足内部任意一点出发可到其他任一点。

可以用来缩点。

code

一些地方有注释。注意这个代码是没fa的。

void tarjan(int u)
{
	dfn[u]=low[u]=++tim;
	vis[u]=1;sta[++top]=u;
	for(int i=head[u]; i; i=nxt[i])
	{
		int v=to[i];
		if(!dfn[v]) tarjan(v),low[u]=min(low[u],low[v]);
		else if(vis[v]) low[u]=min(low[u],dfn[v]);
        //这里是考虑当单点下面挂着环(环指向点)时,单点与环不能属于同一个分量
	}
	if(low[u]==dfn[u])
	{
		scc[u]=++tot;vis[u]=0;
		val[tot]=a[u];
		while(sta[top]!=u)
		{
			scc[sta[top]]=tot;
			val[tot]=min(val[tot],a[sta[top]]);
			vis[sta[top--]]=0;
		}
		--top;
	}
}

割点

定义(这个顺便是有向图还是无向图):删去这个点及其连边,图分为两个不连通的部分。同样不需要记录父亲

code

void tarjan(int u){
	dfn[u]=low[u]=++tim;
	int k=0;
	for(int i=head[u];i!=-1;i=e[i].nxt){
		int v=e[i].to;
		if(!dfn[v]){
			k++;tarjan(v,u);
			low[u]=min(low[u],low[v]);
			if(u!=root&&dfn[u]<=low[v]
			||u==root&&k>1)spot[u]=1;
            //注意到选定u为根节点时,在原图中,其实多个子树被u分割,因此还需特判。
		}
		else low[u]=min(low[u],dfn[v]);
	}
}

定义:删去这条边,图分为两个不连通的部分。

求解思路应该和上面很想,但如果有重边那一定两个都不能是桥,而如果直接写成else if(v!=dad) low[u]=min(low[u],dfn[v]);判不到这个情况,所以改写成else if(i!=(edge^1)) low[u]=min(low[u],dfn[v]);即可。

code

void tarjan(int u,int edge){
	dfn[u]=low[u]=++tim;
	for(int i=head[u];i!=-1;i=e[i].nxt)
    {
		int v=e[i].to;
		if(!dfn[v])
        {
			tarjan(v,i);
			low[u]=min(low[u],low[v]);
			if(dfn[u]<low[v])bridge[i]=1;
		}
		else if(i!=(edge^1)) low[u]=min(low[u],dfn[v]);
                注意要加括号。
	}
}

点双连通分量

定义:一个极大的子图,满足图中删去任何一个点都不会改变图的连通性。

性质:

  1. 点双连通分量中没有割点。

    若干个点双连通分量以割点相连接。

    原图中的每个割点可能属于多个点双连通分量,其它点只可能属于一个点双连通分量。

  2. 任意两个端点之间,存在至少两条不相交的简单路径。

很明显,根据求割点的方法分开点双连通分量即可,割点视实际情况处理。

看一道题:

裸题,求点双的size即可。

code

void tarjan(int u,int fa)
{
	sta[++top]=u;dfn[u]=low[u]=++tim;
	for(int i=head[u]; i; i=nxt[i])
	{
		int v=to[i];if(v==fa) continue;
		if(!dfn[v])
		{
			tarjan(v,u);
			if(low[v]>=dfn[u])
            //这里取大于等于是为了处理一个完整环+一个割点+一个完整环的情况。
			{
				ll sz=2;vis[u]=vis[v]=1;
				while(sta[top]!=v) ++sz,vis[sta[top]]=1,--top;
				--top;ans=ans+sz*(sz-1)/2ll;
			}
			else low[u]=min(low[u],low[v]);
		}
		else low[u]=min(low[u],dfn[v]);
	}
}

The End.

posted @ 2021-11-14 21:09  keepcoder  阅读(52)  评论(1编辑  收藏  举报