【模版】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 }
View Code

 

双联通分量

推荐: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 }
View Code

 

求割边

一条无向边(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 }
View Code

 

posted @ 2017-06-07 21:35  lxtx  阅读(148)  评论(0)    收藏  举报