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;
}

浙公网安备 33010602011771号