题意简述:给定一张有向图,判断是否满足:它是一个强连通图且每条边都属于且仅属于一个环(即仙人掌)。
刚看到这道题,以为仙人掌和欧拉回路有什么神奇的关系,于是把判断欧拉回路的代码交上去,WA到40分...那么我们想想靠谱的做法。首先,判断一张图是否是强连通图是很简单的事情,只要跑一遍tarjan,判断强连通分量个数是否为1即可。如何判断每条边是否都属于且仅属于一个环呢?既然我们已经跑过一遍tarjan,就思考一下tarjan能给我们提供什么信息。在整张图强连通的基础上,要保证每条边都属于且仅属于一个环,容易发现,dfs树上一定不含前向边和横叉边,除此之外,返祖边也需要满足一定的限制:一个结点最多只能有一条返祖边,而且返祖边之间不能出现交叉。怎么判断返祖边之间有无交叉?在当前节点记录当前可以返回的dfs序的最小值(注意“当前”二字,这个值不等于low!),保证反祖边指向的结点的dfs序不能小于该值。要判断这张图是否满足这些条件,我们需要在跑过tarjan后再进行一遍dfs。在这遍dfs中,我们采取给结点染色的方法,所有结点的初始颜色都为0,还没有访问完所有出边的结点颜色为1,访问完所有出边的结点颜色为2。代码如下。
bool dfs(int x,int mdfn,int nback)
//mdfn表示当前可以返回的dfs序的最小值,nback表示从当前结点连出的返祖边数
{
color[x]=1;//开始访问当前结点的出边
for(int i=first[x];i;i=next[i])
if(color[to[i]]==1)//说明i为返祖边
{
nback++;
if(dfn[to[i]]<mdfn) return 0;//反祖边指向的结点的dfs序不能小于mdfn
}
if(nback>1) return 0;//从当前结点连出的返祖边数不能超过1
if(nback) mdfn=low[x];//若有返祖边,更新mdfn,然后才能往下dfs,否则可能传递错误的mdfn
for(int i=first[x];i;i=next[i])
{
if(color[to[i]]==2) return 0;//说明i为前向边或横叉边
if(!color[to[i]]&&!dfs(to[i],mdfn,0)) return 0;//color[to[i]]=0说明i为树边
}
color[x]=2;//当前结点的所有出边已经访问完
return 1;//这张图是仙人掌
}