tarjan求點的雙連通分量

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章