边双连通缩点裸题
因为这是一个连通图,边双连通可以先缩点
这样是一颗树,任意两点间只有一条路
那么统计入度为零的点,用边两两连接起来
每个点的度都大于 2 2 2,这样一定是一个边双
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 2e5+10;
struct edge{
int u,to,nxt;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v)
{ d[++cnt] = (edge){u,v,head[u]},head[u] = cnt; }
int low[maxn],dfn[maxn],id,bridge[maxn];
int bccn,bcc[maxn],n,m,in[maxn];
void tarjan(int u,int fa)
{
dfn[u] = low[u] = ++id;
for(int i=head[u];i;i=d[i].nxt )
{
int v = d[i].to;
if( v==fa ) continue;
if( !dfn[v] )
{
tarjan( v,u );
low[u] = min( low[u],low[v] );
if( low[v]>dfn[u] )
bridge[i] = bridge[i^1] = 1;
}
low[u] = min( low[u],dfn[v] );
}
}
void sd(int u)
{
dfn[u] = 1, bcc[u] = bccn;
for(int i=head[u];i;i=d[i].nxt )
{
int v = d[i].to;
if( bridge[i]||dfn[v] ) continue;
sd(v);
}
}
int main()
{
while( scanf("%d%d",&n,&m)!=EOF )
{
for(int i=1;i<=m;i++)
{
int l,r; scanf("%d%d",&l,&r);
add(l,r); add(r,l);
}
tarjan(1,1);
memset( dfn,0,sizeof(dfn) );
for(int i=1;i<=n;i++)
if( !dfn[i] ) bccn++,sd(i);
for( int i=2;i<=cnt;i+=2 )
{
int u = d[i].u, v = d[i].to;
if( bcc[u]==bcc[v] ) continue;
in[bcc[u]]++, in[bcc[v]]++;
}
int ans = 0;
for(int i=1;i<=bccn;i++)
if( in[i]==1 ) ans++;
printf("%d\n",(ans+1)/2 );
cout << ans << "!\n";
cnt = 1,bccn = id = 0;
for(int i=1;i<=n;i++)
head[i] = dfn[i] = low[i] = bcc[i] = 0;
}
}