[BZOJ2730][HNOI2012]矿厂搭建--tarjan

点双连通分量模板–矿厂搭建


题目传送门:这是BZOJ2730的传送门
题目传送门:这是codevs1996的传送门
题目传送门:这是 洛谷 3225的传送门


抱怨几句

模板来自刘汝佳那本蓝书,me一开始在理解代码的时候,弄错了一个小的判断句,认为其没有卵用…..
然后这道题出现了,让me对那个判断句刮目相看。
啊啊啊啊啊啊啊调了几个小时!!!

·····························


题目分析

题意: 中文题,自己去看←_← (其实是不想打字……)

思路: 题目说,任意一个矿点坍塌之后,其余的矿场到安全通道至少有一条路。
对于一个无向图来说,只有在割点被切掉时,其余点连通性才会改变。
分类讨论:

对于每一堆 “仅含一个割点的点双连通分量”,如果位于割点的矿场坍塌,被分割出来的部分矿场就需要建一个安全通道。

那些“含有两个割点的双连通分量”,就算有一个位于割点的矿场坍塌,这个分量里所有的矿场可以通过另一个割点与外界连通,因此不需要建立安全通道。

如果整个图全部都是双连通分量,即不含割点,那么我们需要建造两个安全通道,一个塌了还可以走另一个

建造方法总数计算也比较简单,如果整个图无割点,那么方案总数乘上C(n,2),不然乘上(n-1) 。其中n是每个连通分量的点个数。


坑点

题目只告诉边数,并没有告诉点数,me们需要在输入的时候对点编号取max,放心编号一定是从1开始且连续。为什么?因为me已经AC了hhhhhhh。
还有就是,可能有多个图。
ps:有8个点只有一个图,数据略水。


代码:
BZOJ: M:860Kb T:0ms L:2786 B

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std ;

int N,maxn,ans,cntn;
long long esc_cnt ;

struct ori_path {
    int fr , pre , to ;
}ori_p[1005] ;
int ori_head[1005] , ori_tp = 1 ;

void In( int t1 , int t2 ){
    ori_p[ ++ori_tp ].pre = ori_head[t1];
    ori_p[ ori_tp ].to = t2 ;
    ori_head[t1] = ori_tp ;
    ori_p[ ori_tp ].fr = t1 ;
}

vector< int > bcc[1005] ;
int bccno[1005] , topp , b_time , dfn[1005] , bcc_cnt , sta[1005] ;
bool is_cut[1005];

int bcc_tarjan( int u , int f ){
    cntn++;
    int lowu = dfn[u] = ++b_time , child = 0 ;
    for( int i = ori_head[u] ; i ; i = ori_p[i].pre ) {
        int v = ori_p[i].to ;
        if( !dfn[v] ){
            sta[ ++topp ] = i ;
            child++ ;
            int lowv = bcc_tarjan( v , u );
            lowu = min ( lowu , lowv );
            if( lowv >= dfn[u] ){
                is_cut[u] = true ;
                bcc_cnt++ ; bcc[ bcc_cnt ].clear() ;
                while(1){
                    ori_path t = ori_p[ sta[ topp-- ] ] ;
                    if( bccno[ t.fr ] != bcc_cnt ){
                        bcc[ bcc_cnt ].push_back( t.fr ) ;bccno[ t.fr ] = bcc_cnt;
                    }
                    if( bccno[ t.to ] != bcc_cnt ){
                        bcc[ bcc_cnt ].push_back( t.to ) ;bccno[ t.to ] = bcc_cnt;
                    }
                    if( t.fr == u && t.to == v )    break;
                }
            }
        }  else if ( dfn[v] < dfn[u] && v != f ){
            sta[ ++topp ] = i ;
            lowu = min( lowu , dfn[v] ) ;
        }
    }
    if ( f < 0 && child == 1 )  is_cut[u] = false ;
    return lowu ;
}

void solve(){
    for( int i = 1 ; i <= bcc_cnt ; ++ i ){
        int cnt_cut = 0 , siz = bcc[i].size() ;
        for(int j = 0 ; j < siz ; ++ j ){
            if( is_cut[ bcc[i][j] ] ) cnt_cut++;
            if( cnt_cut > 1 )   break;
        }
        if( cnt_cut == 1 ){
            esc_cnt *=(long long )(siz - 1 ) ;
            ans += 1 ;
        } else if ( cnt_cut == 0 ){
            ans += 2 ;
            esc_cnt *= (long long )(siz - 1) * siz / 2 ;
        }
    }
}

void clear(){
    ori_tp = 1 ; ans = 0 ; esc_cnt = 1LL ; maxn = 0 ; b_time = 0 ;
    memset( dfn , 0 , sizeof(dfn) ) ;
    memset( bccno , 0 , sizeof(bccno) ) ;
    memset( is_cut , false , sizeof(is_cut) ) ;
    memset( ori_head , 0 , sizeof(ori_head) ) ;
}

int main(){
    int t1 , t2 , cas = 0 ;
    while(scanf("%d",&N)&&N!=0){
        cas++ ; 
        clear();
        for( int i = 1 ; i <= N ; ++i ) {
            scanf( "%d%d" , &t1 , &t2 ) ;
            maxn = max( maxn , t1 );
            maxn = max( maxn , t2 );
            if(t1 != t2){
                In( t1 , t2 ) ; In( t2 , t1 ) ;
            } else ans ++ ;
        }
        for(int i = 1 ; i <= maxn ; ++ i )
            if( !dfn[i] ) {
                bcc_cnt = 0 ; cntn = 0 ; topp = 0;
                bcc_tarjan( i , -1 );solve();
            }
        printf("Case %d: %d %lld\n",cas,ans,esc_cnt);
    }
}

附赠一组小数据

in:
6
1 2
2 3
3 4
4 5
5 6
6 2

out:
Case 1: 2 4

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值