bzoj 2730: [HNOI2012]矿场搭建——tarjan求点双

Description

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

Input

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

Output

输入文件中有多少组数据,输出文件 output.txt 中就有多少行。每行对应一组输入数据的 结果。其中第 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

HINT

 

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

Case 2 的一组解为(4,5,6,7)。
———————————————————————————————
这道题我们发现对于每一个点双联通分量 如果他没有割点 那么他就是一个单独的块 
对于这样的块 我们应该给他设两个出口 防止一个崩了 而对于只有一个联通块的 我们应该设一个不再割点的出口
这样如果出口崩掉了还可以去别的块 而对于有大于一个割点 不用设出口 他可以随便去别的块
然后乘法原理就可以辣 (吐槽:点双写起来贼丑
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using std::max;
using std::min;
const int M=1e3+7;
int read(){
    int ans=0,f=1,c=getchar();
    while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+(c-'0'); c=getchar();}
    return ans*f;
}
int n,m;
int first[M],cnt,buck[M];
struct node{int from,to,next;}e[2*M];
void ins(int a,int b){e[++cnt]=(node){a,b,first[a]}; first[a]=cnt;}
void insert(int a,int b){ins(a,b); ins(b,a);}
int dfn[M],low[M],T,iscut[M];
int hc,sz[M],color[M],stk[M],top;
void clear(){
    n=0; cnt=1; T=0; hc=0; top=0;
    memset(iscut,0,sizeof(iscut));
    memset(first,0,sizeof(first));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(sz,0,sizeof(sz));
    memset(color,0,sizeof(color));
    memset(buck,0,sizeof(buck));
}
void tarjan(int x,int fa){
    low[x]=dfn[x]=++T;
    int child=0;
    for(int i=first[x];i;i=e[i].next){
        int now=e[i].to;
        if(!dfn[now]){
            stk[++top]=i;
            child++;
            tarjan(now,x);
            low[x]=min(low[x],low[now]);
            if(low[now]>=dfn[x]){
                iscut[x]=1;
                hc++;
                while(1){
                    int k=stk[top--];
                    if(color[e[k].from]!=hc){
                        if(color[e[k].from]) buck[color[e[k].from]]++;
                        sz[hc]++; color[e[k].from]=hc;
                    }
                    if(color[e[k].to]!=hc){
                        if(color[e[k].to]) buck[color[e[k].to]]++;
                        sz[hc]++; color[e[k].to]=hc;
                    }
                    if(e[k].from==x&&e[k].to==now) break;
                }
            }
        }
        else if(dfn[now]<dfn[x]&&now!=fa) stk[++top]=i,low[x]=min(low[x],dfn[now]);
    }
    if(fa==-1&&child==1) iscut[x]=0;
}
int main(){
    int x,y,h=0;
    while(scanf("%d",&m)==1&&m){
        ++h; clear();
        for(int i=1;i<=m;i++){
            x=read(); y=read();
            insert(x,y); n=max(n,max(x,y));
        }
        for(int i=1;i<=n;i++)if(!dfn[i]) tarjan(i,-1);
        for(int i=1;i<=n;i++)if(iscut[i]) buck[color[i]]++;
        LL ans=1;
        int tot=0;
        for(int i=1;i<=hc;i++){
            if(buck[i]==1) ans*=max(sz[i]-1,1),tot++;
            if(!buck[i]) ans*=sz[i]*(sz[i]-1)/2,tot+=2;
        }
        printf("Case %d: %d %lld\n",h,tot,ans);
    }
    return 0;
}
View Code

 

posted @ 2017-10-12 15:49  友人Aqwq  阅读(162)  评论(0编辑  收藏  举报