对于一个无向图,如果一个点集,它内部的任意一个点对之间,至少有两条点完全不重复的路径,那么这个点集就是原图的一个点双连通分量,而点双联通分量之间是由割点隔开,割点就是如果删去这个点,原图的连通块数会增加,那么这个点就是割点。
通过tarjan算法,我们可以用一次 dfs 标记出所有的割点以及所有双连通分量。
注释版:
1 #include<stdio.h>
2 #include<string.h>
3 #include<stack>
4 #include<algorithm>
5 #include<vector>
6 using namespace std;
7
8 const int maxn=1e5+5;
9 const int maxm=1e5+5;
10
11 int head[maxn],point[maxm],nxt[maxm],size;
12 int n,t,bcccnt;
13 //n点数,t是dfs的时间轴,bcccnt是双连通分量个数
14 int stx[maxn],low[maxn],bcc[maxn],cut[maxn];
15 //stx是节点在dfs时间轴的位置,low是该点能够通过后继节点到达的最远祖先,bcc是某个点所属的双连通分量编号(割点的编号无效),cut是是否为割点
16 vector<int>bccs[maxn];
17 //双连通分量内的节点
18 stack<int>S;
19
20 void init(){
21 memset(head,-1,sizeof(head));
22 size=0;
23 }
24
25 void add(int a,int b){
26 point[size]=b;
27 nxt[size]=head[a];
28 head[a]=size++;
29 point[size]=a;
30 nxt[size]=head[b];
31 head[b]=size++;
32 }
33
34 void dfs(int s,int pre){
35 stx[s]=low[s]=++t; //记录点的时间轴标号,初始化能访问到的最远祖先节点是自己
36 S.push(s);
37 int son=0; //为了判定根节点是否是割点
38 for(int i=head[s];~i;i=nxt[i]){
39 int j=point[i];
40 if(!stx[j]){
41 son++;
42 dfs(j,s);
43 low[s]=min(low[s],low[j]); //用子节点的low值更新自己
44 if(low[j]>=stx[s]){ //如果子节点最远只能访问自己或后继节点,则出现双连通分量
45 cut[s]=1; //自己是割点
46 bcccnt++;
47 bccs[bcccnt].clear();
48 while(1){
49 int u=S.top();S.pop();
50 bcc[u]=bcccnt;
51 bccs[bcccnt].push_back(u);
52 if(u==j)break;
53 }
54 bcc[s]=bcccnt;
55 bccs[bcccnt].push_back(s);
56 }
57 }
58 else if(j!=pre)low[s]=min(stx[j],low[s]);
59 }
60 if(pre==-1&&son==1)cut[s]=0; //若根节点只有一个子节点,则不是割点
61 }
62
63 void setbcc(){
64 memset(cut,0,sizeof(cut));
65 memset(stx,0,sizeof(stx));
66 memset(bcc,0,sizeof(bcc));
67 t=bcccnt=0;
68 for(int i=1;i<=n;++i)if(!stx[i])dfs(i,-1);
69 }
70
71 int main(){
72 int m;
73 scanf("%d%d",&n,&m);
74 init();
75 while(m--){
76 int a,b;
77 scanf("%d%d",&a,&b);
78 add(a,b);
79 }
80 setbcc();
81 for(int i=1;i<=n;++i){
82 printf("%d:%d %d\n",i,cut[i],bcc[i]);
83 }
84 for(int i=1;i<=bcccnt;++i){
85 printf("%d:",i);
86 for(int j=0;j<bccs[i].size();++j){
87 printf("%d ",bccs[i][j]);
88 }
89 printf("\n");
90 }
91 return 0;
92 }
无注释版:
1 #include<stdio.h>
2 #include<string.h>
3 #include<stack>
4 #include<algorithm>
5 #include<vector>
6 using namespace std;
7
8 const int maxn=1e5+5;
9 const int maxm=1e5+5;
10
11 int head[maxn],point[maxm],nxt[maxm],size;
12 int n,t,bcccnt;
13 int stx[maxn],low[maxn],bcc[maxn],cut[maxn];
14 vector<int>bccs[maxn];
15 stack<int>S;
16
17 void init(){
18 memset(head,-1,sizeof(head));
19 size=0;
20 }
21
22 void add(int a,int b){
23 point[size]=b;
24 nxt[size]=head[a];
25 head[a]=size++;
26 point[size]=a;
27 nxt[size]=head[b];
28 head[b]=size++;
29 }
30
31 void dfs(int s,int pre){
32 stx[s]=low[s]=++t;
33 S.push(s);
34 int son=0;
35 for(int i=head[s];~i;i=nxt[i]){
36 int j=point[i];
37 if(!stx[j]){
38 son++;
39 dfs(j,s);
40 low[s]=min(low[s],low[j]);
41 if(low[j]>=stx[s]){
42 cut[s]=1;
43 bcccnt++;
44 bccs[bcccnt].clear();
45 while(1){
46 int u=S.top();S.pop();
47 bcc[u]=bcccnt;
48 bccs[bcccnt].push_back(u);
49 if(u==j)break;
50 }
51 bcc[s]=bcccnt;
52 bccs[bcccnt].push_back(s);
53 }
54 }
55 else if(j!=pre)low[s]=min(stx[j],low[s]);
56 }
57 if(pre==-1&&son==1)cut[s]=0;
58 }
59
60 void setbcc(){
61 memset(cut,0,sizeof(cut));
62 memset(stx,0,sizeof(stx));
63 memset(bcc,0,sizeof(bcc));
64 t=bcccnt=0;
65 for(int i=1;i<=n;++i)if(!stx[i])dfs(i,-1);
66 }
67
68 int main(){
69 int m;
70 scanf("%d%d",&n,&m);
71 init();
72 while(m--){
73 int a,b;
74 scanf("%d%d",&a,&b);
75 add(a,b);
76 }
77 setbcc();
78 for(int i=1;i<=n;++i){
79 printf("%d:%d %d\n",i,cut[i],bcc[i]);
80 }
81 for(int i=1;i<=bcccnt;++i){
82 printf("%d:",i);
83 for(int j=0;j<bccs[i].size();++j){
84 printf("%d ",bccs[i][j]);
85 }
86 printf("\n");
87 }
88 return 0;
89 }