emmm,拖了好久的割点割边,今天终于有时间来写一下模板了233
【定义】
在一个无向图中,如果有一个顶点集合,删除这个顶点集合以及这个集合中所有顶点相关联的边以后,图的连通分量增多,就称这个点集为割点集合,如果某个割点集合只含有一个顶点 X(也即{X}是一个割点集合),那么X称为一个割点。
类似的,在一个无向图中,如果有一个边集合,删除这个边集合以后,图的连通分量增多,就称这个边集为割边集合,如果某个割边集合只含有一条边 X(也即{X}是一个边集合),那么X称为一个割边,也叫做桥。
【Tarjan算法】
对没错,Tarjan算法就可以用来求割点割边
我们先回顾一下Tarjan算法的精髓--- dfn 和 low
- dfn(x)为节点 x 搜索的次序编号(时间戳);
- low(x)为 x 或 x 的子树能够追溯到的最早的祖先的次序号。
【割点】
对于一个点 u,有以下两种情况:
- 如果 u 是根节点,那么当它有多于一个子树时,它就是割点
- 如果 u 不是根节点,并且 u 为 v 在搜索树中的父亲,当 dfn[ u ] ≤ low[ v ] 时,它就是割点
【割边】
一条边(u,v)是割边,当且仅当 u 为 v 在搜索树中的父亲,并且 dfn[ u ] < low[ v ]
在实现的时候,由于有重边的存在,我们还要用一个数组记录一下树枝边
【更新 low 值】
起初,low[ u ] = dfn [ u ],表示能追溯到的最早祖先就是 u
现有一条边(u,v)
如果 dfn[ v ] == 0,那么就继续dfs,回溯的时候 low[ u ] = min ( low[ u ] , low[ v ] )
如果 dfn[ v ] != 0,那么 low[ u ] = min ( low[ u ] , dfn[ v ] )
【模板】
这道题就是道纯模板题
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define M 200005
using namespace std;
int n,m,t,num,root;
int first[N],v[M],next[M];
int dfn[N],low[N],point[N];
bool cut[N];
void add(int x,int y)
{
t++;
next[t]=first[x];
first[x]=t;
v[t]=y;
}
void Tarjan(int x)
{
int i,j,children=0;
num++;
dfn[x]=num;
low[x]=num;
for(i=first[x];i;i=next[i])
{
j=v[i];
if(!dfn[j])
{
children++;
Tarjan(j);
low[x]=min(low[x],low[j]);
if(x==root&&children>1) cut[x]=true;
if(dfn[x]<=low[j]&&x!=root) cut[x]=true;
}
else
low[x]=min(low[x],dfn[j]);
}
}
int main()
{
int x,y,i,j,ans=0;
scanf("%d%d",&n,&m);
for(i=1;i<=m;++i)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
for(i=1;i<=n;++i)
if(!dfn[i])
root=i,Tarjan(i);
for(i=1;i<=n;++i)
if(cut[i])
point[++ans]=i;
printf("%d\n",ans);
for(i=1;i<=ans;++i)
printf("%d ",point[i]);
return 0;
}
这道题的意思是,给出一个有桥图,我们要加边将它变成边双联通图,求最少要加几条边
具体做法就是将所有边双联通都缩为一个点,将缩完点的图看做一棵树,求出叶节点的个数 ans
最后的答案就是(ans + 1)/ 2
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 5005
#define M 20005
using namespace std;
int n,m,num,t=1;
int first[N],u[M],v[M],next[M];
int du[N],dfn[N],low[N],from[N],father[N];
bool bridge[M];
int find(int x)
{
if(father[x]!=x)
father[x]=find(father[x]);
return father[x];
}
void add(int x,int y)
{
t++;
next[t]=first[x];
first[x]=t;
u[t]=x;
v[t]=y;
}
void Tarjan(int x)
{
int i,j;
num++;
dfn[x]=num;
low[x]=num;
for(i=first[x];i;i=next[i])
{
j=v[i];
if(i==(from[x]^1))
continue;
if(!dfn[j])
{
from[j]=i;
Tarjan(j);
low[x]=min(low[x],low[j]);
if(dfn[x]<low[j])
{
bridge[from[j]]=true;
bridge[from[j]^1]=true;
}
}
else
low[x]=min(low[x],dfn[j]);
}
}
int main()
{
int x,y,i,j,ans=0;
scanf("%d%d",&n,&m);
for(i=1;i<=m;++i)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
Tarjan(1);
for(i=1;i<=n;++i)
father[i]=i;
for(i=2;i<=t;i+=2)
if(!bridge[i])
father[find(u[i])]=find(v[i]);
for(i=2;i<=t;i+=2)
if(bridge[i])
du[find(u[i])]++,du[find(v[i])]++;
for(i=1;i<=n;++i)
if(find(i)==i&&du[i]==1)
ans++;
printf("%d",(ans+1)/2);
return 0;
}