无向图的割顶和割边模板题 P1637&&P1638

Description

割顶(割点或关节点):如果在图G中删去一个点v,连通分量数量增加,则称v为G的割顶。
本题问题:给出含n个节点m条边的连通图,请计算这个图的割顶集。


Input

第一行包含两个整数:n和m,分别表示图的节点数量(编号为1..n)和m条边。接下来的m行,每行包含两个整数u,v,表示一条边。


Output

第一行输出num,表示割点数目。第二行包含num个整数,每个整数表示一个割点的编号(由小到大输出)。


Hint

开800000。


Solution

定理 1:无向连通图的DFS树中,树根结点是两个或更多子树时,它才是割顶。

定理 2:无向连通图的DFS树中,非根结点i是割顶当且i存在一个儿j,使得j及其后代都没有返祖边连回到它的祖先(连回i不算)。

low[i]=min(low[i],dfn[j]);如果i的儿子的时间戳比i小,那么说明i的儿子j一定存在一条返祖边,那么i就一定不是割点。
low[i]=min(low[i],low[j]);如果j没有进去过,那么就把low[i]更新成他儿子的时间戳和他自己的最小值。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 200005
using namespace std;
struct Edge{
	int u;
	int v;
	int next;
	int id;
}edge[maxn];
int first[maxn],last[maxn],dfn[maxn],low[maxn],cut[maxn];
int node,dfn_TimeClock,n,m,x,y,cnt;
bool vis[maxn];
void addedge(int u,int v,int id){
	edge[++node]=(Edge){u,v,0,id};
	if(first[u]==0)first[u]=node;
	else edge[last[u]].next=node;
	last[u]=node;
}
void init(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		scanf("%d%d",&x,&y);
		addedge(x,y,i);
		addedge(y,x,i);
	}
}
void tarjan_(int i,int fd){
	vis[i]=true;
	dfn[i]=low[i]=++dfn_TimeClock;
	int xxx=0;
	for(int p=first[i];p;p=edge[p].next){
		int j=edge[p].v,id=edge[p].id;
		if(vis[j]){
			if(dfn[j]<=dfn[i]&&id!=fd){
				low[i]=min(low[i],dfn[j]);
			}
			continue;
		}
		xxx++;
		tarjan_(j,id);
		low[i]=min(low[i],low[j]);
		if(low[j]>=dfn[i])cut[i]=1;
	}
	if(fd==0&&xxx==1)cut[i]=0;
}
int main(){
	init();
	for(int i=1;i<=n;i++){
		if(!vis[i])tarjan_(i,0);
	}
	for(int i=1;i<=n;i++){
		if(cut[i]==1)cnt++;
	}
	printf("%d\n",cnt);
	for(int i=1;i<=n;i++){
		if(cut[i])printf("%d ",i);
	}
	return 0;
}

Description

割边(桥):如果在图G中删去一条边e,连通分量数量增加,则称e为G的割边。
本题问题:给出含n个节点m条边的连通图,请计算这个图的割边。


Input

第一行包含两个整数: n和m,分别表示图的节点数量(编号为1..n)和m条边(可能有重边或自环)。接下来的m行,每行包含两个整数u,v,表示一条边。


Output

第一行输出num,num表示割边数目。接下来的num行,每行表示一条割边(u,v),要求,u由小到大,如果u相等则v由小到大。


Hint

开800000。


Solution

在求割点算法中,当由i及其后代走访完毕后,如果low[i]dfn[i],则i的父边是割边(根结点没有父边)。因为当low[i]dfn[i]时,说明i及其后代必须没有连接到i的祖先的返组边存在,即i及其后代必须通过i的父边才能与其他结点连通,所以i的父边是割边。

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<iostream>
#define maxn 800005
using namespace std;
struct Edge{
	int u;
	int v;
	int next;
	int id;
}edge[maxn];
struct anss{
	int leftt;
	int rightt;
	friend bool operator < (anss a,anss b){
		if(a.leftt<b.leftt)return true;
		else if(a.leftt==b.leftt)return a.rightt<b.rightt;
		else return false;
	}
}ans[maxn];
int first[maxn],last[maxn],dfn[maxn],low[maxn];
int node,n,m,x,y,dfn_TimeClock,cnt;
bool vis[maxn],mark[maxn];
void addedge(int u,int v,int id){
	edge[++node]=(Edge){u,v,0,id};
	if(first[u]==0)first[u]=node;
	else edge[last[u]].next=node;
	last[u]=node;
}
void init(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		scanf("%d%d",&x,&y);
		addedge(x,y,i);
		addedge(y,x,i);
	}
}
void tarjan_T(int i,int fd){
	vis[i]=true;
	dfn[i]=low[i]=++dfn_TimeClock;
	for(int p=first[i];p;p=edge[p].next){
		int j=edge[p].v,id=edge[p].id;
		if(vis[j]){
			if(dfn[j]<=dfn[i]&&id!=fd){
				low[i]=min(low[i],dfn[j]);
			}
			continue;
		}
		tarjan_T(j,id);
		low[i]=min(low[i],low[j]);
	}
	if(low[i]==dfn[i]&&fd!=0)mark[fd]=true;
}
int main(){
	init();
	for(int i=1;i<=n;i++){
		if(!vis[i])tarjan_T(i,0);
	}
	for(int i=1;i<=n;i++){
		for(int p=first[i];p;p=edge[p].next){
			int j=edge[p].v;
			if(mark[edge[p].id]&&i<j){
				ans[++cnt]=(anss){i,j};
			}
		}
	}
	sort(ans+1,ans+cnt+1);
	printf("%d\n",cnt);
	for(int i=1;i<=cnt;i++){
		printf("%d %d\n",ans[i].leftt,ans[i].rightt);
	}
	return 0;
}
posted @ 2018-11-30 17:11  虚拟北方virtual_north。  阅读(334)  评论(1编辑  收藏  举报