无向图的割顶和割边模板题 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;
}