时间复杂度:\(O(n+m)\)
用途:有向图缩环
int f[N],dfn[N],low[N],sta[N],top; /*dfn[u]:遍历到u点的时间; low[u]:u点可到达的各点中最小的dfn[v],即最高层的点*/ bool ins[N]; inline void tarjan(int u){ dfn[u]=low[u]=++cnt; sta[++top]=u;ins[u]=true; for(int i=g[u];i;i=e[i].nxt) if(!dfn[e[i].to]){ tarjan(e[i].to); low[u]=min(low[u],low[e[i].to]); } else if(ins[e[i].to]) low[u]=min(low[u],low[e[i].to]); if(dfn[u]==low[u]){ while(sta[top+1]!=u){ f[sta[top]]=u; ins[sta[top--]]=false; } } } inline void solve(){ for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); }
割边,又称桥.
\(dfn[\;],low[\;]\)的定义同\(tarjan\)求有向图强连通分量.
枚举当前点\(u\)的所有邻接点\(v\):
1.如果某个邻接点\(v\)未被访问过,则访问\(v\),并在回溯后更新\(low[u]=min(low[u],low[v])\);
2.如果某个邻接点\(v\)已被访问过,则更新\(low[u]=min(low[u],dfn[v])\).
对于当前节点\(u\),如果邻接点中存在一点\(v\)满足\(low[v]>dfn[u]\)(\(v\)向上无法到达\(u\)及\(u\)祖先)说明\((u,v)\)为一条割边.
inline void tarjan(int u,int fa){ dfn[u]=low[u]=++cnt; for(int i=g[u];i;i=e[i].nxt) if(!dfn[e[i].to]){ tarjan(e[i].to,u); low[u]=min(low[u],low[e[i].to]); if(low[e[i].to]>dfn[u]) cut[e[i].n]=true; } else if(e[i].to!=fa) low[u]=min(low[u],dfn[e[i].to]); }
\(dfn[\;],low[\;]\)的定义同\(tarjan\)求有向图强连通分量.
枚举当前点\(u\)的所有邻接点\(v\):
1.如果某个邻接点\(v\)未被访问过,则访问\(v\),并在回溯后更新\(low[u]=min(low[u],low[v])\);
2.如果某个邻接点\(v\)已被访问过,则更新\(low[u]=min(low[u],dfn[v])\).
对于当前节点\(u\),
如果\(u\)为搜索树中的根节点,若它的子节点数\(\geq2\)(根是多棵子树上节点的唯一连通方式),则\(u\)为割点;
如果\(u\)为搜索树上的非根节点,若存在子节点\(v\)满足\(low[v]\;\geq\;dfn[u]\)(\(v\)向上无法到达\(u\)的祖先),则\(u\)为割点.
inline void tarjan(int u,int fa){ dfn[u]=low[u]=++cnt; for(int i=g[u];i;i=e[i].nxt){ ++t[u]; if(!dfn[e[i].to]){ tarjan(e[i].to,u); low[u]=min(low[u],low[e[i].to]); if(u==rt){ if(t[u]>=2) cut[u]=true; } else if(low[e[i].to]>=dfn[u]) cut[u]=true; } else if(e[i].to!=fa) low[u]=min(low[u],dfn[e[i].to]); } }
2017-01-26 19:18:26