HNOI 2012 矿场搭建

题目描述

煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。

请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。

输入输出格式

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

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

输入输出样例

输入样例#1: 复制
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
输出样例#1: 复制
Case 1: 2 4
Case 2: 4 1
说明

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

Case 2 的一组解为(4,5,6,7)。

分类讨论,如果联通块内部无割点,则在联通块内任选两个点作为出口。
如果联通块内有一个割点,则在联通块内选一个点作为出口。
如果割点大于等于两个,则可与其他联通,不需要出口。

#include<iostream>
#include<cstdio>
#include<cstring> 

using namespace std;
const int MAXN = 505;

struct Edge {
    int nxt,to;
} edge[MAXN*MAXN];

int n,m,tot,num,cnt,countt,group,Num;
int dfn[MAXN],low[MAXN],head[MAXN],Cut,vis[MAXN];
long long sum=1;
bool cut[MAXN];

inline void add(int bg,int ed) {
    edge[++cnt].to=ed;
    edge[cnt].nxt=head[bg];
    head[bg]=cnt;
}

inline void tarjan(int u,int fa) {
    int child=0;
    dfn[u]=low[u]=++num;
    for(register int i=head[u]; i; i=edge[i].nxt) {
        int v=edge[i].to;
        if(!dfn[v]) {
            tarjan(v,fa);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u] && u!=fa)
                cut[u]=1;
            if(u==fa)
                child++;
        }
        low[u]=min(low[u],dfn[v]);
        if(child>=2 && u==fa)   
            cut[u]=1;
    }
}

inline void dfs(int u){
    vis[u]=group;
    Num++;
    for(register int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].to;
        if(cut[v] && vis[v]!=group){
            Cut++;
            vis[v]=group;
        }
        if(!vis[v])
            dfs(v);
    }
}

int main() {
    while(~scanf("%d",&m)) {
        countt++;
        if(m==0) break;
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(head,0,sizeof(head));
        memset(cut,false,sizeof(cut));
        memset(vis,false,sizeof(vis));
        num=cnt=tot=group=n=0;sum=1;
        for(register int i=1; i<=m; i++) {
            int x,y;
            scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
            n=max(n,max(x,y));
        }
        tarjan(1,1);
        for(register int i=1;i<=n;i++){
            if(!cut[i] && !vis[i]){
                ++group;
                Cut=Num=0;
                dfs(i);
                if(Cut==0){
                    tot+=2;
                    sum*=(Num-1)*Num/2;
                }
                if(Cut==1){
                    tot++;
                    sum*=Num;
                }
            }
        }

        printf("Case %d: %d %lld
",countt,tot,sum);
    }
}
原文地址:https://www.cnblogs.com/sdfzsyq/p/9677138.html