Description
煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。
题解:
求出哪些是割点后分类讨论咯。1、没有割点,那么至少设置2个,方案数为 C2n (这个显然)。2、有割点,那就看一下由割点分开的连通块,若一个连通块只经过一个割点,那么这个连通块内必须设置1个出口(否则割点出事故之后就没有出口),方案数乘上这个连通块内的点数;否则不用设置出口,因为任意一点出事故这个连通块还是可以连向其它。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
const int Maxn=1010;
const int Maxm=2010;
struct Edge{int y,next;}e[Maxm];
int last[Maxn],len;
int n,dfn[Maxn],low[Maxn],id,cnt,sta[Maxn],top;
bool cut[Maxn],in[Maxn];
void ins(int x,int y)
{
int t=++len;
e[t].y=y;e[t].next=last[x];last[x]=t;
}
void Tarjan(int x,int fa)
{
int ch=0;
low[x]=dfn[x]=++id;
sta[++top]=x;in[x]=true;
for(int i=last[x];i;i=e[i].next)
{
int y=e[i].y;
if(y==fa)continue;
if(!dfn[y])
{
Tarjan(y,x);
ch++;
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x])cnt++,cut[x]=true;//这种情况说明y无法回到x祖先,所以若x不为根,x为割点
}
else if(in[y])low[x]=min(low[x],dfn[y]);
}
if(!fa&&ch==1)cnt--,cut[x]=false;//处理x为根的情况
if(low[x]==dfn[x])
{
int i;
do
{
i=sta[top--];
in[i]=false;
}while(i!=x);
}
}
LL size=0;
void dfs(int x,int ti)
{
dfn[x]=1;size++;
for(int i=last[x];i;i=e[i].next)
{
int y=e[i].y;
if(cut[y]&&dfn[y]!=ti)dfn[y]=ti,cnt++;
else if(!dfn[y])dfs(y,ti);
}
}
int main()
{
int Case=0;
while(1)
{
scanf("%d",&n);
if(!n)break;Case++;
memset(last,0,sizeof(last));len=cnt=0;
memset(dfn,0,sizeof(dfn));id=0;
memset(low,0,sizeof(low));
memset(cut,false,sizeof(cut));
memset(in,false,sizeof(in));top=0;
int nn=0;
for(int i=1;i<=n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
ins(x,y);ins(y,x);
nn=max(nn,max(x,y));
}
for(int i=1;i<=nn;i++)
if(!dfn[i])Tarjan(i,0);
int ans1=0;LL ans2=1LL;
if(!cnt)ans1=2,ans2=nn*(nn-1)/2;
else
{
memset(dfn,0,sizeof(dfn));
for(int i=1;i<=nn;i++)
if(!dfn[i]&&!cut[i])
{
size=cnt=0;
dfs(i,i);
if(cnt==1)ans1++,ans2*=size;
}
}
printf("Case %d: %d %lld\n",Case,ans1,ans2);
}
}