题意: 链接.
方法: 点双联通分量(割点).
解析:
首先这道题有两个问,并且是无向图
第一问是任意删掉一个点后,满足剩下所有的点均可以找到一个出口,则最少安放的出口数是多少?
第二问是满足最少安防的出口数有几种方案?
首先先把图画出来看第一个样例
我们发现他所选取的四个点分别都是把1删掉后,所处于两个不同的块中的两点,而1恰好为其中的割点,观察第二组样例后,依旧如此。
则第一问就可以转化为:删掉割点后有多少个块?
然而这并不是正确的,因为我们发现,如果一个块与两个割点相连,那么即使其中的一个割点是坍塌点,这个块仍旧能与另一个块组成在一起。所以所求的块应该是仅包含一个割点的块的个数。
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std ;
struct node
{
int from ;
int to ;
int next ;
};
node edge[1001] ;
int head[1001] ;
int deep[1001] ;
int low[1001] ;
int f[1001] ;
int belong[1001] ;
int v[1001] ;
int pt[1001] ;
int num[1001] ;
int cnt , tot , cnt_root , root , ans;
void init()
{
memset(head , -1 , sizeof(head)) ;
memset(deep , 0 , sizeof(deep)) ;
memset(low , 0 , sizeof(low)) ;
memset(f , 0 , sizeof(f)) ;
memset(num , 0 , sizeof(num)) ;
cnt = 1 , tot = 0 , ans = 0;
}
void init2()
{
memset(deep , 0 , sizeof(deep)) ;
memset(low , 0 , sizeof(low)) ;
memset(belong , 0 , sizeof(belong)) ;
memset(pt , 0 , sizeof(pt)) ;
tot = 0 ;
}
void edgeadd(int from , int to)
{
edge[cnt].from = from ;
edge[cnt].to = to ;
edge[cnt].next = head[from] ;
head[from] = cnt ++ ;
}
void tarjan(int x , int fa)
{
deep[x] = low[x] = ++tot ;
for(int i = head[x] ; i != -1 ; i = edge[i].next)
{
int to = edge[i].to ;
if(to == fa) continue ;
if(!deep[to])
{
tarjan(to , x) ;
low[x] = min(low[to] , low[x]) ;
if(low[to] >= deep[x])
{
if(x == root) cnt_root ++ ;
else f[x] ++ ;
}
}else low[x] = min(low[x] , deep[to]) ;
}
}
void tarjan2(int x , int fa)
{
deep[x] = low[x] = ++tot ;
for(int i = head[x] ; i != -1 ; i = edge[i].next)
{
int to = edge[i].to ;
if(to == fa || f[to]) continue ;
if(!deep[to])
{
tarjan2(to , x) ;
low[x] = min(low[to] , low[x]) ;
}else low[x] = min(low[x] , deep[to]) ;
}
belong[x] = ans ;
num[ans] ++ ;
}
int m , n;
int main()
{
int tot = 0 ;
while(scanf("%d" , &m) && m != 0)
{
tot ++ ;
n = 0 ;
init() ;
for(int i = 1 ; i <= m ; i++)
{
int x , y ;
scanf("%d%d" , &x , &y) ;
edgeadd(x , y) ;
edgeadd(y , x) ;
n = max(x , n) ;
n = max(y , n) ;
}
for(int i = 1 ; i <= n ; i++)
{
if(!deep[i])
{
cnt_root = 0 , root = i ;
tarjan(i , 0) ;
if(cnt_root >= 2) f[i] = cnt_root - 1 ;
}
}
init2() ;
for(int i = 1 ; i <= n ; i++)
{
if(!deep[i] && !f[i])
{
ans ++ ;
tarjan2(i , 0) ;
}
}
for(int i = 1 ; i <= n ; i++)
{
if(f[i])
{
memset(v , 0 , sizeof(v)) ;
for(int j = head[i] ; j != -1 ; j = edge[j].next)
{
int to = edge[j].to ;
if(!v[belong[to]])
{
pt[belong[to]] ++ ;
v[belong[to]] = 1 ;
}
}
}
}
long long tmp = 1 , tmp2 = 0;
for(int i = 1 ; i <= ans ; i++)
{
if(pt[i] <= 1)
{
tmp *= num[i] ;
tmp2 ++ ;
}
}
if(ans == 1)
{
printf("Case %d: %d %d\n" , tot , 2 , n *(n-1) / 2) ;
}else
{
printf("Case %d: %lld %lld\n" , tot , tmp2 , tmp) ;
}
}
}