首页 > 代码库 > 矿场搭建(codevs 1996)

矿场搭建(codevs 1996)

题目描述 Description

煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。
请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。

输入描述 Input Description

输入文件有若干组数据,每组数据的第一行是一个正整数N(N≤500),表示工地的隧道数,接下来的N 行每行是用空格隔开的两个整数S 和T,表示挖煤点S 与挖煤点T 由隧道直接连接。输入数据以0 结尾。

输出描述 Output Description

输入文件中有多少组数据,输出文件中就有多少行。每行对应一组输入数据的结果。其中第i 行以Case i: 开始(注意大小写,Case 与i 之间有空格,i 与:之间无空格,:之后有空格),其后是用空格隔开的两个正整数,第一个正整数表示对于第i 组输入数据至少需要设置几个救援出口,第二个正整数表示对于第i 组输入数据不同最少救援出口的设置方案总数。输入数据保证答案小于2^64。输出格式参照以下输入输出样例。

样例输入 Sample Input

9
1 3
4 1
3 5
1 2
2 6
1 5
6 3
1 6
3 2
6
1 2
1 3
2 4
2 5
3 6
3 7
0

样例输出 Sample Output

Case 1: 2 4
Case 2: 4 1

数据范围及提示 Data Size & Hint

Case 1 的四组解分别是(2,4),(3,4),(4,5),(4,6);
Case 2 的一组解为(4,5,6,7)。

/*
  隔点会把此图分成若干个连通块。
  如果一个连通块与两个及以上的割点相连,就不建;与一个相连,就建一个,没有相连的,就建两个。方案数用乘法原理。
*/
#include<cstdio>
#include<iostream>
#include<cstring>
#define N 510
using namespace std;
int head[N],n,m,cas;
int num[N],low[N],in[N],s[N],ok[N],sum[N],r[N],used[N],vis[N],top,tot,indexx;
struct node
{
    int v,pre;
};node e[N*2];
void clear()
{
    memset(e,0,sizeof(e));
    memset(head,-1,sizeof(head));
    memset(num,0,sizeof(num));
    memset(low,0,sizeof(low));
    memset(in,0,sizeof(in));
    memset(s,0,sizeof(s));
    memset(ok,0,sizeof(ok));
    memset(sum,0,sizeof(sum));
    memset(r,0,sizeof(r));
    memset(used,0,sizeof(used));
    memset(vis,0,sizeof(vis));
    top=tot=indexx=n=0;
}
void add(int i,int x,int y)
{
    e[i].v=y;
    e[i].pre=head[x];
    head[x]=i;
}
void tarjan(int u,int fa)
{
    int x=0;
    num[u]=low[u]=++indexx;
    in[u]=1;s[++top]=u;
    for(int i=head[u];i!=-1;i=e[i].pre)
    {
        if(fa==(i^1))continue;
        int v=e[i].v;
        if(!num[v])
        {
            x++;
            tarjan(v,i);
            low[u]=min(low[u],low[v]);
            if(low[v]>=num[u])ok[u]=1;
        }
        else if(in[v])
          low[u]=min(low[u],num[v]);
    }
    if(fa==-1&&x<2)ok[u]=0;
}
void dfs(int x)
{
    vis[x]=1;sum[tot]++;
    for(int i=head[x];i!=-1;i=e[i].pre)
    {
        int y=e[i].v;
        if(vis[y])continue;
        if(ok[y])
        {
            if(used[y]!=tot)
            {
                used[y]=tot;r[tot]++;
            }
        }
        else dfs(y);
    }
}
int main()
{
    while(scanf("%d",&m))
    {
        if(!m)break;clear();
        for(int i=1;i<=m;i++)
        {
            int x,y;scanf("%d%d",&x,&y);
            add(i*2-2,x,y);add(i*2-1,y,x);
            n=max(n,max(x,y));
        }
        for(int i=1;i<=n;i++)
          if(!num[i])tarjan(i,-1);
        for(int i=1;i<=n;i++)
          if(!vis[i]&&!ok[i])
          {
              ++tot;dfs(i);
          }
        int ans1=0;long long ans2;
        if(tot==1)
        {
            ans1=2;
            ans2=(long long)(sum[1]*(sum[1]-1)/2);
        }
        else
        {
            ans2=1;
            for(int i=1;i<=tot;i++)
              if(r[i]==1)ans1++,ans2*=(long long)sum[i];
        }
        printf("Case %d: %d ",++cas,ans1);cout<<ans2<<endl;
    }
    return 0;
} 

 

矿场搭建(codevs 1996)