[luoguP3325][HNOI2012]矿场搭建

P3225 [HNOI2012]矿场搭建

题目描述

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

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

输入输出格式

输入格式:

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

输出格式:

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

首先这个题首先我们讨论两种情况,第一种情况就是这个图本来就是一个点连通图,也就是说不存在割点。那么我们任意去掉一个点以后,这个图仍然是联通的。那么我们的逃生出口一定要有两个,保证如果一个逃生出口Over了,还有另外一个逃生出口可以保命。所以总方案数就是Cn2 。

然后第二种情况,这个图存在割点。那么如果坍塌的是这个割点,这个图就会变得不连通。对于那种只和一个割点相连的块,我们至少要在这个块里面选出来一个建逃生通道,而且也是任意选择。假设这个块有p个节点,那么该块的方案数就是Cp-11。(有一个割点所以-1)(p为该块的点数)

针对第二种情况总结来看对于每一个双联通分量我们一共有三种情况:

1.如果这个双联通分量里面没有割点。那么在这个双联通分量里面需要建Cp2个出口。

2.如果双联通分量只有一个割点,那么再非割点的地方任意选择一个点来建逃生出口都没有问题。方案数为Cp-11。

3.若该双联通分量里面有两个割点,那么我们就不需要建立了,因为无论哪一个节点hehe了,其他的点都可以通过其他的没有hehe的点跑到别的双联通分量里面去。

然后以上是针对一个双联通分量的,那么至于总共的方案,我们就可以利用一下组合数学了.....(qwq)。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define INF 0x7fffffff
#define MAXN 100010
#define ll long long
using namespace std;
int n,m;
struct point{
    int from;
    int to;
    int next;
}edge[MAXN*2];
int total,head[MAXN];
void add(int f,int t){
    total++;
    edge[total].from=f;
    edge[total].to=t;
    edge[total].next=head[f];
    head[f]=total;
}
int Yeasion[MAXN],Nein[MAXN];
int stack[MAXN],top,ken;
vector<int>block[MAXN];
int cut[MAXN],cnt,root;
void Tarjan(int now,int fa){
    Yeasion[now]=Nein[now]=++ken;
    stack[++top]=now; int child=0;
    for(int i=head[now];i;i=edge[i].next){
        if(!Yeasion[edge[i].to]){
            Tarjan(edge[i].to,now); child++;
            Nein[now]=min(Nein[now],Nein[edge[i].to]);
            if(now==root&&child>1) cut[now]=1;
            if(now!=root&&Yeasion[now]<=Nein[edge[i].to]) cut[now]=1;
            if(Yeasion[now]<=Nein[edge[i].to]){
                cnt++; block[cnt].clear();
                int pass;
                do{
                    pass=stack[top--];
                    block[cnt].push_back(pass);
                }while(pass!=edge[i].to);/////
                block[cnt].push_back(now);
            }
        }
        else Nein[now]=min(Nein[now],Yeasion[edge[i].to]);
    }
}
void Solve(){
    n=0;
    for(int i=1;i<=m*2;i++){
        Yeasion[i]=Nein[i]=0;
        head[i]=0;
        cut[i]=0;
    }top=ken=cnt=0;
    for(int i=1;i<=m;i++){
        int x; int y;
        scanf("%d%d",&x,&y);
        n=max(n,max(x,y));
        add(x,y); add(y,x);
    };
    for(int i=1;i<=n;i++){
        if(!Yeasion[i]){
            root=i;
            Tarjan(i,-1);
        }
    }
    ll res=0,num=1;
    for(int i=1;i<=cnt;i++){
        int yor=0;
        int len=block[i].size();
        for(int j=0;j<len;j++)
            if(cut[block[i][j]]) yor++;
        if(yor==0) res+=2,num=num*(len-1)*len/2;
        else if(yor==1) res++,num=num*(len-1);
    }
    printf("%lld %lld\n",res,num);
}
int main(){
    int t=1;
    while(scanf("%d",&m)&&m){
        printf("Case %d: ",t++);
        Solve();
    } return 0;
}

猜你喜欢

转载自www.cnblogs.com/Yeasio-Nein/p/P3325.html
今日推荐