描述
给定一个n个点m条边的无向连通图,求该图中“点双连通分量”的个数。
输入
第一行,n, m。
接下来m行,每行两个整数u,v,表示一条无向边(u,v)
对于100%的数据,2<=n,m<=2 * 10^5。
输出
输出点双连通分量的数量。
题解
点双连通分量:去掉割点后还剩下几个分开的联通块;
割点:去掉后图被分为多块联通块
用tarjan算法,从根节点开始,通过每一个节点只访问一次的方法建一颗搜索树,用dfn(为第几个被访问的点),low(可连接的编号最低的点)来作为比较条件
我们可以看每一个割点有几个分支来判断
#include<bits/stdc++.h>
using namespace std;
vector<int> a[210000];
int low[210000],dfn[210000],sum=1,k;
int root;
void f(int x)
{
dfn[x]=low[x]=++k;
int child=0;
for(int j=0;j<a[x].size();j++)
{
int y=a[x][j];
if(!dfn[y])
{
f(y);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x])
{
child++;
if(x!=root||child>1)
sum++;//因为每次都会深搜所有可能,所以每一次被调取都是在不同的连通块
}
}
else
low[x]=min(low[x],dfn[y]);
}
}
int fl[21000];
int find(int x)
{
int r=x,i;
while(x!=fl[x]) x=fl[x];
while(r!=fl[r])
{
i=fl[r];
fl[r]=x;
r=i;
}
return x;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) fl[i]=i;
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
a[x].push_back(y);
a[y].push_back(x);
int fx=find(x);
int fy=find(y);
if(fx!=fy)
{
fl[fx]=fy;
}
}
for(int i=1;i<=n;i++)
{
if(fl[i]==i)
{
root=i;
f(i);
}
}
printf("%d\n",sum);
return 0;
}