题目链接:https://vjudge.net/problem/UVA-1108
题意:有一座地下的稀有金属矿由n条隧道和一些连接点组成,其中每条隧道连接两个连接点。任意两个连接点之间最多只有一条隧道。为了降低矿工的危险,你的任务是在一些连接点处安装太平井和相应的逃生装置,使得不管哪个连接点倒塌,不在此连接点的所有矿工都能到达太平井逃生(假定除了倒塌的连接点外,其他隧道和连接点完好无损)。为了节约成本,你应当在尽量少的连接点安装太平井。还要需要计算出太平井的数目最小时的安装方案总数。
分析:本题的模型是:在一个无向图上选择尽量少的点涂黑(对应太平井),使得任意删除一个点后,每个连通分量至少有一个黑点。不难发现,把割顶涂黑是不划算的。进一步分析,当一个点-双连通分量只有一个割顶时才需要涂,而且是任选一个非割顶涂黑即可。两个问题同时解决。
一个特殊情况是整个图没有割顶。此时需要涂两个点,方案总数是V*(V-1)/2,其中V是连接点的个数。
代码变量名看不懂的话可以参考这篇博客的变量名注释:https://blog.csdn.net/qq_36300700/article/details/81188107
代码:
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <stdlib.h>
#include <algorithm>
#include <math.h>
#include <vector>
#include <string.h>
#include <stack>
using namespace std;
const int maxn =50000+10;
int n,m;
int pre[maxn],iscut[maxn],dfs_clock,bcc_cnt,bccno[maxn];
vector <int> G[maxn],bcc[maxn];
void init()
{
dfs_clock=bcc_cnt=0;
memset(pre,0,sizeof(pre));
memset(iscut,0,sizeof(iscut));
memset(bccno,0,sizeof(bccno));
for(int i=1; i<maxn; i++)
G[i].clear();
}
struct Edge
{
int u,v;
Edge(int u, int v):u(u),v(v){};
};
stack<Edge> S;
int dfs(int u,int fa)
{
int lowu = pre[u] =++dfs_clock;
int child =0;
for(int i=0; i<G[u].size(); i++)
{
int v= G[u][i];
Edge e = (Edge){u,v};
if(!pre[v])
{
S.push(e);
child++;
int lowv = dfs(v,u);
lowu=min(lowu,lowv);
if(lowv >= pre[u])
{
iscut[u]=1;
bcc_cnt++;
bcc[bcc_cnt].clear();
for(; ;)
{
Edge x=S.top();
S.pop();
if(bccno[x.u]!=bcc_cnt)
{
bcc[bcc_cnt].push_back(x.u);
bccno[x.u]=bcc_cnt;
}
if(bccno[x.v]!=bcc_cnt)
{
bcc[bcc_cnt].push_back(x.v);
bccno[x.v]=bcc_cnt;
}
if(x.u==u&&x.v==v)
break;
}
}
}
else if(pre[v]<pre[u]&&v!=fa)
{
S.push(e);
lowu = min(lowu,pre[v]);
}
}
if(fa<0&&child==1)
iscut[u]=0;
return lowu;
}
int main()
{
int kase = 0;
while(scanf("%d",&m)&&m)
{
init();
int u,v;
n=-1;
for(int i=0; i<m; i++)
{
scanf("%d %d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
n=max(n,max(u,v));
}
for(int i=1; i<=n; i++)
{
if(!pre[i])
dfs(i,-1);
}
long long ans1=0,ans2=1;
for(int i=1; i<=bcc_cnt; i++)
{
int cut_cnt =0;
for(int j=0; j<bcc[i].size(); j++)
{
if(iscut[bcc[i][j]])
cut_cnt++;
}
if(cut_cnt==1)
{
ans1++;
ans2*=(long long )(bcc[i].size()-cut_cnt);
}
}
if(bcc_cnt==1)
{
ans1=2; ans2=bcc[1].size()*(bcc[1].size()-1)/2;
}
printf("Case %d: %lld %lld\n",++kase,ans1,ans2);
}
return 0;
}