首先最少数量显然大于1,那么考虑炸掉一个点,如果它不是割点并没有什么影响,如果是割点的话必须要满足炸开之后的每个联通块都有至少一个救援点。如果这个图没有割点的话选两个点放就行了,答案是C(n,2),否则求出每一个割点,把这个图按割点分割成若干个联通块,把这些联通块缩点就是一个树结构,要使树上任何一条边断掉之后分成的两块都有救援点,就一定要在每一个叶子块上放一个救援点。把所有叶子块的size乘起来就行了。
#include<cstdio>
#include<iostream>
#include<memory.h>
#define N 1005
#define ll unsigned long long
#define clr(a) memset(a,0,sizeof(a))
#define t1 st[top]
using namespace std;
struct edge{
int e,next;
}ed[N*2];
int n,m,i,j,ans1,ne,s,e,tim,ecc,cnt,a[N],dfn[N],low[N],size,cut[N];
ll ans2;
void add(int s,int e)
{
ed[++ne].e=e;ed[ne].next=a[s];a[s]=ne;
}
void tarjan(int x,int fa)
{
dfn[x]=low[x]=++tim;
int cnt1=0;
bool f=false;
for (int j=a[x];j;j=ed[j].next)
if (ed[j].e!=fa)
{
if (!dfn[ed[j].e]) tarjan(ed[j].e,x),cnt1++;
low[x]=min(low[x],low[ed[j].e]);
if (dfn[x]<=low[ed[j].e]) f=true;
}
if ((fa==-1&&cnt1>1)||(fa!=-1&&f)) cut[x]=1,cnt++;
}
void dfs(int x,int tim)
{
dfn[x]=1;size++;
for (int j=a[x];j;j=ed[j].next)
{
if (cut[ed[j].e]&&dfn[ed[j].e]<tim) cnt++,dfn[ed[j].e]=tim;
if (!dfn[ed[j].e]) dfs(ed[j].e,tim);
}
}
int main()
{
freopen("2730.in","r",stdin);
freopen("2730.out","w",stdout);
for (int t=1;;t++)
{
scanf("%d",&m);
if (!m) return 0;
n=ne=tim=ans1=cnt=0;
clr(a);clr(dfn);clr(cut);
for (i=1;i<=m;i++)
{
scanf("%d%d",&s,&e);
add(s,e);add(e,s);
n=max(n,max(s,e));
}
for (i=1;i<=n;i++) if (!dfn[i]) tarjan(i,-1);
ans2=1ll;
clr(dfn);
if (cnt==0) ans1=2,ans2=(n*(n-1)/2);
else
for (i=1;i<=n;i++)
{
cnt=0;
if (!cut[i]&&!dfn[i]) size=0,dfs(i,i);
if (cnt==1) ans2*=size,ans1++;
}
printf("Case %d: %d %llu\n",t,ans1,ans2);
}
}