井下矿工

20 篇文章 0 订阅
13 篇文章 0 订阅

井下矿工

问题描述

有一座地下的稀有金属矿由n条隧道和一些连接点组成,其中每条隧道连接两个连接点。任意两个连接点之间最多只有一条隧道。为了降低矿工的危险,你的任务是在一些连接点处安装太平井和相应的逃生装置,使得不管哪个连接点倒塌,不在此连接点的所有矿工都能到达太平井逃生(假定除倒塌的连接点不能通行外,其他隧道和连接点完好无损)。为了节约成本,你应当在尽量少的连接点安装太平井。你还需要计算出在太平井的数目最小时的安装方案总数。

输入格式

输入包含多组数据。每组数据第一行为隧道的条数n(n<=50000),一下n行,每行两个整数,即一条隧道两段的连接点的编号(所有连接点从1开始编号)。每组数据的所有连接点保证连通。
输入以数字0作为结束

输出格式

对于每组数据,输出两个整数,即最少需要安装的太平井数目以及对应的方案总数。
答案保证在64位带符号整数范围以内。

思路

意思就是,给一个简单无向图G,求点p的个数,使得任意一个点及其相关的边被去掉之后,剩下的每一个点都能够到达这些p点。

最简单的情况:G本身就是一个双连通分量,这时安装的太平井数目就是2个。

一般的情况:对于一般的图GG,可以近似认为它是由若干个双连通分量组成的,那么每一个双连通分量内就应该安放两个太平井。显然,割点上面不应该放太平井,如图。如果这个双连通分量内只有两个点,那么其中必定有一个是割点,所以这种情况下只用放一个太平井。

这里写图片描述
【黄点是割点】

这样一来,这题的思路就很清晰了:找图G内有几个双连通分量,乘以2,然后除去割点的数目,就是太平井的数目;方案数乘起来就可以了。

代码

代码冗杂,仅供对拍

#include<iostream>
#include<cstdio>
#include<stack>
#include<vector>
#define N 100050
#define ll long long
using namespace std;
stack<ll>s;
vector<ll>ve[N];
ll Next[N],Last[N],End[N],St[N],cnt;
ll Low[N],Dfn[N],Time,Son[N],cut[N],bcc,Belong[N];
bool used[N];
void Ins(ll x,ll y){
    End[++cnt]=y;
    St[cnt]=x;
    Next[cnt]=Last[x];
    Last[x]=cnt;
}
void Tarjan(ll u,ll fa){
    Time++;
    Low[u]=Time,Dfn[u]=Time;
    for(ll i=Last[u];i;i=Next[i]){
        ll v=End[i];
        if(!Dfn[v]){
            Son[u]++;//注意此处的Son[u]指的是以u为树根的搜索树的儿子,不是G内的父子关系
            s.push(i);
            Tarjan(v,u);
            Low[u]=min(Low[u],Low[v]);
            if(Dfn[u]<=Low[v]){
                cut[u]=true,bcc++;
                ll x,y;
                do{
                    ll k=s.top();s.pop();
                    x=St[k],y=End[k];
                    if(Belong[x]!=bcc)
                        ve[bcc].push_back(x),Belong[x]=bcc;
                    if(Belong[y]!=bcc)
                        ve[bcc].push_back(y),Belong[y]=bcc;
                }while(!(x==u && y==v));
            }
        }else if(Dfn[u]>Dfn[v] && v!=fa)
            Low[u]=min(Low[u],Dfn[v]),s.push(i);
    }
    if(!fa && Son[u]<=1)cut[u]=false;
}
void Clean(){
    for(ll i=0;i<N;i++){
        Next[i]=0,Last[i]=0,End[i]=0,St[i]=0;
        Low[i]=0,Dfn[i]=0,Son[i]=0,cut[i]=0;
        Belong[i]=0,used[i]=0;
        ve[i].clear();
    }
    while(!s.empty())s.pop();
    cnt=0,Time=0,bcc=0;
}
int kkk(){
    ll n,t=0;
    while(1){
        scanf("%lld",&n);
        if(!n)break;
        ll tot=0;t++;
        for(ll i=1;i<=n;i++){
            ll x,y;
            scanf("%lld%lld",&x,&y);
            Ins(x,y),Ins(y,x);
            if(!used[x])used[x]=true,tot++;
            if(!used[y])used[y]=true,tot++;
        }
        Tarjan(1,0);
        ll Ans1=0,Ans2=1;
        for(ll i=1;i<=bcc;i++){
            ll c=0,_Size=ve[i].size();
            for(ll j=0;j<_Size;j++){
                if(cut[ve[i][j]])c++;
            }
            if(c!=1)continue;
            Ans1++,Ans2*=(_Size-1);
        }
        if(bcc==1)Ans1=2,Ans2=(tot*(tot-1))/2;
        printf("Case %lld: %lld %lld\n",t,Ans1,Ans2);
        Clean();
    }
    return 0;
}
//扩栈代码
const int main_stack=16;    
char my_stack[128<<20];    
int main() {
    __asm__("movl %%esp, (%%eax);\n"::"a"(my_stack):"memory");    
    __asm__("movl %%eax, %%esp;\n"::"a"(my_stack+sizeof(my_stack)-main_stack):"%esp");    
    kkk();    
    __asm__("movl (%%eax), %%esp;\n"::"a"(my_stack):"%esp");    
    return 0; //add stack copy copy!!!   
}   
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值