BZOJ 2730: [HNOI2012]矿场搭建

18 篇文章 0 订阅
1 篇文章 0 订阅

Description

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

Solution

警察叔叔就是这道Dog Birth题搞了我一个下午
n的范围是500可是怎么看我的做法都是 O(n) 的==
首先对于那些不是割点的点我们没必要管他是不是
所以我们可以先tarjan跑出割点。
然后把割点都删了,看看多出多少联通块。
然后扫描联通块,如果某联通块与大于等于2个割点相连,那么这个联通块多半是废了就没意义了是不是
然后如果只与一个割点相连,那么答案可能性要乘上联通块的size,块数要++
所以 O(n) 就够了呀==
割(智)点(商)敲(不)错(够)调死我了==

现在重申一下割点的得到方法:
首先像一般tarjan那样,得到pre和lowlink数组。
然后对于以某节点 v ,如果它有一个子节点p  lowlink[p]>=pre[v]那么这个点 v <script type="math/tex" id="MathJax-Element-10">v</script>是割点。
不知为何调了很久?

Code

/**************************************************************
    Problem: 2730
    User: bblss123
    Language: C++
    Result: Accepted
    Time:4 ms
    Memory:1308 kb
****************************************************************/

#include<iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
#include<vector>
using namespace std;
typedef long long ll;
const int M=505;
int n,m,txt=0;
int dfs_clock,pre[M],lowlink[M],sccid[M],allc;
inline void Min(int &a,int b){if(a>b)a=b;}
inline void Max(int &a,int b){if(a<b)a=b;}
#define vec vector<int>
#define pb push_back
vec G[M];
bool mark[M];
int flag[M];
void predfs(int v,const int &rt){
    pre[v]=lowlink[v]=++dfs_clock;
    int p=mark[v]=0;
    for(int i=0;i<G[v].size();++i){
        int to=G[v][i];
        if(pre[to])Min(lowlink[v],pre[to]);
        else{
            predfs(to,rt),Min(lowlink[v],lowlink[to]);
            mark[v]|=lowlink[to]>=pre[v];
            ++p;
        }
    }
    if(v==rt&&p<=1)mark[v]=0;
}
inline void rd(int &a){
    a=0;char c;
    while(c=getchar(),!isdigit(c));
    do a=a*10+(c^48);
        while(c=getchar(),isdigit(c));
}
int sz,cnt;
void dfs(int v,const int &c){
    flag[v]=c;
    ++sz;
    for(int i=0;i<G[v].size();++i){
        int to=G[v][i];
        if(flag[to]==c)continue;
        if(mark[to]){
            flag[to]=c,++cnt;
            continue;
        }
        dfs(to,c);
    }
}
inline void Init(){
    memset(mark,0,sizeof(mark));
    memset(flag,0,sizeof(flag));
    memset(pre,0,sizeof(pre));
    memset(lowlink,0,sizeof(lowlink));
    memset(sccid,0,sizeof(sccid));
    for(int i=1;i<=n;++i)G[i].clear();
    dfs_clock=0,allc=0;
}
inline void gao(){
    n=0;
    for(int i=1,a,b;i<=m;++i){
        rd(a),rd(b);
        G[a].pb(b),G[b].pb(a);
        Max(n,a),Max(n,b);
    }
    for(int i=1;i<=n;++i)
        if(!pre[i])predfs(i,i);
    int res=0;ll ans=1;
    for(int i=1;i<=n;++i){
        if(mark[i]||flag[i])continue;
        sz=cnt=0;
        dfs(i,i);
        if(cnt>=2)continue;
        ans*=sz,++res;
    }
    if(res==1)printf("Case %d: 2 %d\n",++txt,n*(n-1)>>1);
    else printf("Case %d: %d %lld\n",++txt,res,ans);
    Init();
}
int main(){
    for(;scanf("%d",&m),m;)gao();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值