PS:本文图均用链式前向星存储
一.连通分量(模板)
1.有向图
inline void tarjan(int p)
{
dfn[p]=low[p]=++dfn_t;
q.push(p);
for(int i=head[p];i;i=edge[i].next)
{
int y=edge[i].to;
if(!dfn[y])
{
tarjan(y);
low[p]=min(low[p],low[y]);
}else
if(!belong[y])//是否在栈内
low[p]=min(low[p],dfn[y]);
}
if(dfn[p]==low[p])
{
//++scc_t;
for(int x=0;x!=p;)
{
x=q.top();
q.pop();
belong[x]=p;
// or col[x]=scc_t;
}
}
}
2.无向图
(与有向图的唯一区别就是判断前驱,去环)
inline void tarjan(int u,int pre)
{
dfn[u]=low[u]=++dfn_t;
q.push(u);
for(int i=head[u];i;i=edge[i].next)
if(edge[i].to!=pre)//不回去
{
int v=edge[i].to;
if(!dfn[v])
{
tarjan(v,u);
low[u]=min(low[u],low[v]);
}else
//if(!belong[v]) 网上有种说法,貌似用有向图的方法也可以判断
if(dfn[v]<dfn[u])
low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u])
{
for(int x=-1;x!=u;)
{
x=q.top();
q.pop();
belong[x]=u;
}
}
}
二.缩点
1.关于标记/染色 ( belong[]/col[] )
-目测有两种方式:
-第一种直接标记成u
for(int x=-1;x!=u;)
{
x=q.top();
q.pop();
belong[x]=u;
}
-第二种用联通块个数标记
++scc_t;
for(int x=-1;x!=u;)
{
x=q.top();
q.pop();
col[x]=scc_t;
}
2.关于缩点方式
ps:缩点后注意每次判断 col[x]!=col[y] || belong[x]!=belong[y]
-在原图上修改(移花接木) 比较喜欢第一种,省内存
PS:手打并未测试(可画图理解)
for(int i=1;i<=n;++i)
if(i!=belong[i])
{
for(int j=head[i];j;)
if(belong[i]!=belong[edge[j].to])
{
int tmp=edge[j].next;
edge[j].next=head[belong[i]];
head[belong[i]]=j;
j=tmp;
}
}
-重建图
for(int i=1;i<=n;++i)
for(int j=head1[i];j;j=edge1[j].next)
if(col[i]!=col[edge1[j].to])
{
int y=edge1[j].to;
add(col[i],col[y],cnt2,head2,edge2);
}
欢迎dalao指出错误