tarjan相关问题
图的 dfs
如果是无向图,求 dfs 树,需要记录每条边及其反边是否被遍历过(包括树边和非树边),无法简单的记录父亲或父亲边编号来判断。
概述
- 有向图都需要栈,无向图都不需要(求点双,圆方树除外)。
- 无向图和有向图的差异在
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);
}
}
}