【模版】tarjan算法
强连通分量
推荐:https://www.byvoid.com/zht/blog/scc-tarjan/
Low(u)=Min
{
Dfn(u),
Low(v),(u,v)为树枝,u为v的父节点
Dfn(v),(u,v)为指向栈中节点的后向边(非横叉边)
}
1 #include<cstdio> 2 #include<cstring> 3 #define repu(i,x,y) for(i=x;i<=y;i++) 4 #define N 2001 5 #define min(a,b) (a<b?a:b) 6 struct edge{int v,next;}e[N]; 7 int dfn[N],low[N],f[N],a[N],first[N],x=0,cnt=0,tp=0; 8 void add(int u,int v) { 9 e[tp]=(edge){v,first[u]}; first[u]=tp++; 10 } 11 void tarjan(int u) { 12 f[u]=1; dfn[u]=low[u]=++cnt; a[++x]=u; 13 for (int i=first[u];i!=-1;i=e[i].next) { 14 int v=e[i].v; 15 if (!dfn[v]) { 16 tarjan(v); 17 low[u]=min(low[u],low[v]); 18 } 19 if (f[v]) low[u]=min(low[u],dfn[v]); 20 } 21 if (dfn[u]==low[u]) { 22 while (a[x]!=u) f[a[x]]=0,printf("%d ",a[x--]); 23 printf("%d\n",a[x--]); f[u]=0; 24 } 25 } 26 int main() { 27 int n,m,i,u,v; scanf("%d%d",&n,&m); 28 memset(first,-1,sizeof(first)); 29 repu(i,1,m) 30 scanf("%d%d",&u,&v),add(u,v); 31 repu(i,1,n) 32 if (!dfn[i]) tarjan(i); 33 }
双联通分量
推荐:https://www.byvoid.com/zht/blog/biconnect
求割点
一个顶点u是割点,当且仅当满足(1)或(2)
(1) u为树根,且u有多与一个子树。
(2) u不为树根,且满足存在(u,v)为树枝边,使得Dfn(u)<=Low(v)。
1 #include<cstdio> 2 #include<cstring> 3 #define N 200001 4 #define repu(i,x,y) for(i=x;i<=y;i++) 5 #define min(a,b) (a<b?a:b) 6 struct edge{int v,next;}e[N]; 7 int dfn[N],low[N],a[N],f[N],first[N],l[N],x=0,cnt=0,ans=0,tp=0; 8 void add(int u,int v) { 9 e[tp]=(edge){v,first[u]}; first[u]=tp++; 10 e[tp]=(edge){u,first[v]}; first[v]=tp++; 11 } 12 void tarjan(int u,int fa) { 13 dfn[u]=low[u]=++cnt; f[u]=1; a[++x]=u; int t=0; 14 for (int i=first[u];i!=-1;i=e[i].next) { 15 int v=e[i].v; 16 if (!dfn[v]) { 17 tarjan(v,i); ++t; 18 if (low[v]>=dfn[u]&&fa!=-1) if (!l[u]) l[u]=1,ans++; 19 low[u]=min(low[u],low[v]); 20 } 21 if (f[v]&&i!=(fa^1)) low[u]=min(low[u],dfn[v]); 22 } 23 if (fa==-1&&t>=2) if (!l[u]) l[u]=1,ans++; 24 if (dfn[u]==low[u]) { 25 while (a[x]!=u) f[a[x]]=0,printf("%d ",a[x--]); 26 printf("%d\n",a[x--]); f[u]=0; 27 } 28 } 29 int main() { 30 memset(first,-1,sizeof(first)); 31 int n,m,i,x,y; scanf("%d%d",&n,&m); 32 repu(i,1,m) scanf("%d%d",&x,&y),add(x,y); 33 repu(i,1,n) 34 if (!dfn[i]) tarjan(i,-1); 35 printf("%d\n",ans); 36 repu(i,1,n) if (l[i]) 37 printf("%d ",i); 38 }
求割边
一条无向边(u,v)是桥,当且仅当(u,v)为树枝边,且满足Dfn(u)<Low(v)。
1 #include<cstdio> 2 #include<cstring> 3 #define N 200001 4 #define repu(i,x,y) for(i=x;i<=y;i++) 5 #define min(a,b) (a<b?a:b) 6 struct edge{int v,next;}e[N]; 7 int dfn[N],low[N],vis[N],a[N],first[N],f[N],l[N],lx=0,x=0,cnt=0,tp=0; 8 void add(int u,int v) { 9 e[tp]=(edge){v,first[u]}; first[u]=tp++; 10 e[tp]=(edge){u,first[v]}; first[v]=tp++; 11 } 12 void tarjan(int u,int fa) { 13 dfn[u]=low[u]=++cnt; f[u]=1; a[++x]=u; 14 for (int i=first[u];i!=-1;i=e[i].next) { 15 int v=e[i].v; 16 if (!dfn[v]) { 17 tarjan(v,i); 18 if (low[v]>dfn[u]) printf("%d-%d\n",u,v); 19 low[u]=min(low[u],low[v]); 20 } 21 if (f[v]&&i!=(fa^1)) low[u]=min(low[u],dfn[v]); 22 } 23 if (dfn[u]==low[u]) { 24 while (a[x]!=u) f[a[x--]]=0; 25 f[a[x--]]=0; 26 } 27 } 28 int main() { 29 memset(first,-1,sizeof(first)); 30 int n,m,i,x,y; scanf("%d%d",&n,&m); 31 repu(i,1,m) scanf("%d%d",&x,&y),add(x,y); 32 repu(i,1,n) 33 if (!dfn[i]) tarjan(i,-1); 34 }

浙公网安备 33010602011771号