tarjan求点的双连通分量

来源:互联网 发布:csgo 电击枪 知乎 编辑:程序博客网 时间:2024/05/16 08:22

hiho详解链接:http://hihocoder.com/contest/hiho55/problem/1


点的双连通分量的定义:对于一个无向图的子图,当删除其中任意一个点后,不改变图内点的连通性,这样的子图叫做点的双连通子图。而当子图的边数达到最大时,叫做点的双连通分量。


可以知道的是,桥一定是点的双连通分量。


void pop_stack(int x)//边不断出栈并标记,直到编号x的边离开栈为止{num_node++;int len = 0,minn = x;e_vis[x] = 1;while(top > 0 && sta[top] != x){fuben[len++] = sta[top];e_vis[sta[top]] = 1;//不断标记边minn = min(minn,sta[top]);//本题要求,记录同一组内边标号的最小值,可省略top--;}top--;fuben[len++] = x;for(int i = 0;i < len;i++)mark[fuben[i]] = minn;//标记原边所在的组,可省略}//dfs_pos为dfs序,low为最近祖先的dfs序,这里的边中要多加个变量id来便于边的标记//vis为这个点有没被访问void tarjan(int u,int pre){int son_num = 0;int v;vis[u] = 1;dfs_pos[u] = low[u] = ++jilu;//jilu为从的dfs序for(int i = head[u];~i;i = e[i].nex){v = e[i].v;if(e_vis[e[i].id])continue;//如果边被访问过则跳过,if(!vis[v]){sta[++top] = e[i].id;son_num++;tarjan(v,u);low[u] = min(low[u],low[v]);  /*if(pre == -1 && son_num > 1){pop_stack(e[i].id);}*///这题并不需要这句话,某些题目判断时可能需要if(low[v] >= dfs_pos[u]){pop_stack(e[i].id);//边的出栈处理操作}}else if(v != pre){sta[++top] = e[i].id;low[u] = min(low[u],low[v]);}}}



hiho原题的题解代码:

#include <bits/stdc++.h>#define mem(a,b) memset(a,b,sizeof(a))#define pii pair<int,int>typedef long long ll;using namespace std;const int maxn = 250005;struct ppp{int v,nex,id;}e[maxn];int head[maxn / 10],tole,n,m,vis[maxn / 10],jilu;int dfs_pos[maxn / 10],low[maxn / 10];int mark[maxn],e_vis[maxn],top;void make_edge(int u,int v,int id){e[tole].id = id;e[tole].v = v;e[tole].nex = head[u];head[u] = tole++;}int sta[maxn];int fuben[maxn];int num_node;void pop_stack(int x)//边不断出栈并标记,直到编号x的边离开栈为止{num_node++;int len = 0,minn = x;e_vis[x] = 1;while(top > 0 && sta[top] != x){fuben[len++] = sta[top];e_vis[sta[top]] = 1;//不断标记边minn = min(minn,sta[top]);//本题要求,记录同一组内边标号的最小值,可省略top--;}top--;fuben[len++] = x;for(int i = 0;i < len;i++)mark[fuben[i]] = minn;//标记原边所在的组,可省略}//dfs_pos为dfs序,low为最近祖先的dfs序,这里的边中要多加个变量id来便于边的标记//vis为这个点有没被访问void tarjan(int u,int pre){int son_num = 0;int v;vis[u] = 1;dfs_pos[u] = low[u] = ++jilu;//jilu为从的dfs序for(int i = head[u];~i;i = e[i].nex){v = e[i].v;if(e_vis[e[i].id])continue;//如果边被访问过则跳过,if(!vis[v]){sta[++top] = e[i].id;son_num++;tarjan(v,u);low[u] = min(low[u],low[v]);  /*if(pre == -1 && son_num > 1){pop_stack(e[i].id);}*///这题并不需要这句话,某些题目判断时可能需要if(low[v] >= dfs_pos[u]){pop_stack(e[i].id);//边的出栈处理操作}}else if(v != pre){sta[++top] = e[i].id;low[u] = min(low[u],low[v]);}}}int main(){while(~scanf("%d%d",&n,&m)){tole = 0;mem(head,-1);mem(vis,0);mem(e_vis,0);num_node = 0;for(int i = 1,a,b;i <= m;i++){scanf("%d%d",&a,&b);make_edge(a,b,i);make_edge(b,a,i);}top = 0;jilu = 0;mem(mark,0);tarjan(1,-1);printf("%d\n",num_node);for(int i = 1;i <= m;i++){if(i > 1)printf(" ");cout<<mark[i];}printf("\n");}}


0 0