版权声明:我这么弱的蒟蒻,虽然博文不是很好,但也请标明转发地址喵! https://blog.csdn.net/ACerAndAKer/article/details/82839399
无向图tarjan求割点
很显然,我们求出所有联通分量后,若这个联通分量内有
1个割点,那么只能在这个点上建出口,因为这个点坏了其他点就出不去了
2+个割点,那么这个联通分量是安全的,因为其中一个坏了还能走另一个
0个割点,那么这个联通分量至少需要两个出口,否则也无法出去
然后方案数就根据上面这些乱搞一下就好了
代码
//By AcerMo
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lli long long int
using namespace std;
const int M=200500;
int n,m,ans,emm;
int to[M],nxt[M],head[M],cnt;
int dfn[M],low[M],cut[M],fa[M],ind,ti;
inline int read()
{
int x=0;char ch=getchar();
while (ch>'9'||ch<'0') ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline void add(int x,int y)
{
to[++cnt]=y;nxt[cnt]=head[x];head[x]=cnt;
to[++cnt]=x;nxt[cnt]=head[y];head[y]=cnt;
return ;
}
inline void tarjan(int x,int f)
{
low[x]=dfn[x]=++ind;int son=0;
for (int i=head[x];i;i=nxt[i])
if (!dfn[to[i]])
{
tarjan(to[i],x);son++;
low[x]=min(low[x],low[to[i]]);
if (low[to[i]]>=dfn[x]) cut[x]=1;
}
else if (f!=to[i]) low[x]=min(low[x],dfn[to[i]]);
if (son==1&&!f) cut[x]=0;
return ;
}
inline void dfs(int x)
{
fa[x]=ti;
if (cut[x]) return ;emm++;
for (int i=head[x];i;i=nxt[i])
{
if (fa[to[i]]!=ti&&cut[to[i]]) ans++,fa[to[i]]=ti;
if (!fa[to[i]]&&!cut[to[i]]) dfs(to[i]);
}
return ;
}
inline void clean()
{
fill(low,low+n+1,0);
fill(dfn,dfn+n+1,0);fill(cut,cut+n+1,0);
fill(head,head+n+1,0);fill(fa,fa+n+1,0);
return (void)(ti=ind=n=cnt=0);
}
signed main()
{
int cas=0;
while ((m=read())&&m)
{
int x,y;clean();
lli qlm=0,que=1;
for (int i=1;i<=m;i++)
{
x=read(),y=read();
add(x,y),n=max(n,max(x,y));
}
for (int i=1;i<=n;i++)
if (!dfn[i]) tarjan(i,0);
for (int i=1;i<=n;i++)
if (!fa[i]&&!cut[i])
{
ti++;ans=emm=0;dfs(i);
if (!ans) qlm+=2,que*=(emm*(emm-1)/2);
if (ans==1) qlm++,que*=emm;
}
printf("Case %d: %lld %lld\n",++cas,qlm,que);
}
return 0;
}