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");
}
}