题解:
一道水题交了5次才过……
只需考虑删去的点是割点的情况。
删去所有割点,形成若干联通块,假如图中只有一个联通块,则需设置两个出口。
若一个联通块可以到达两个割点,则该联通块内不必设置出口,否则必须设置出口。
方案数利用乘法原理计算即可。
一定要注意细节。
Code:Code:
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#define N 100005
#define ll long long
using namespace std;
int low[N],dfn[N],deep,flag[N],head[N],tot,vis[N],b[N],a[N],cnt,sum,c[N];
struct node
{
int vet,next;
}edge[N];
void add(int u,int v)
{
edge[++tot].vet=v;
edge[tot].next=head[u];
head[u]=tot;
}
void tarjan(int u,int father)
{
int son=0;
low[u]=dfn[u]=++deep;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].vet;
if(!dfn[v])
{
son++;
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u])flag[u]=true;
}else if(v!=father)low[u]=min(low[u],dfn[v]);
}
if(father==-1&&son==1)flag[u]=false;
}
void dfs(int u)
{
vis[u]=false;
cnt++;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].vet;
if(vis[v])dfs(v);else
if(flag[v]&&b[v])sum++,b[v]=false;
}
}
int main()
{
int m,tt=0;
scanf("%d",&m);
while(m!=0)
{
int n=0;tot=0;
memset(head,-1,sizeof(head));
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
n=max(n,u);n=max(n,v);
}
deep=0;
memset(dfn,0,sizeof(dfn));
memset(flag,false,sizeof(flag));
for(int i=1;i<=n;i++)
if(!dfn[i])tarjan(i,-1);
memset(vis,true,sizeof(vis));
int p=0;
for(int i=1;i<=n;i++)
if(flag[i])vis[i]=false;
for(int i=1;i<=n;i++)
{
memset(b,true,sizeof(b));
cnt=sum=0;
if(vis[i])
{
dfs(i);
a[++p]=cnt;
c[p]=sum;
}
}
int ans1;ll ans2;
if(p==1)
{
ans1=2;
ans2=(a[1]-1)*a[1]/2;
}else
{
ans1=0,ans2=1;
for(int i=1;i<=p;i++)
if(c[i]==1)
ans1++,ans2*=a[i];
}
printf("Case %d: %d %lld\n",++tt,ans1,ans2);
scanf("%d",&m);
}
return 0;
}