洛谷P3225 HNOI2012 矿场搭建

题目描述

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

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

输入输出格式

输入格式:

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

输出格式:

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

输入输出样例

输入样例#1: 复制
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
输出样例#1: 
Case 1: 2 4
Case 2: 4 1


这道题是我在不会双联通分量时写的(虽然是COPY的题解)
于是有了一个割点TARJAN的思路

首先跑一个TARJAN求出所有割点
然后我本来想对于每一个割点的周围进行DFS 但是发现这样写不出来(没法判重也没法计数)
于是看了题解 =_= 发现他是对于每一个块进行DFS 在割点处停止以划分块
设一个块里有NUM个不是割点的点
对于每个块 如果有2个及以上割点 则不需要建出口
    如果有1个割点 则要建一个出口 方案数为NUM
      如果没有割点 则要建2个出口 方案数为NUM*(NUM-1)/2
在搜索每个块的时候记得要染色而不是访问(=——=)
因为访问的话
  1. 如果你标记割点 将导致另一个块遍历不到这个割点
  2. 如果你不标记割点 将导致当前块其他点找到这个割点时重复计数

但是染色就不会有上面的问题 跑一遍爽就完事了 这题数据十分的水

上满分代码(本蒟蒻调了40MIN的说 = =)

#include<bits/stdc++.h>
#define MAXN 20010
#define MAXM 100010

using namespace std;

struct Edge{
    int to,nxt;
}edge[MAXM*2];
int head[MAXN],ectr,n,m;
void addedge(int from,int to){
    ectr++;
    edge[ectr].to = to;
    edge[ectr].nxt = head[from];
    head[from] = ectr;
}
long long NUM,CUT,ans1,ans2,color;
int dfn[MAXN],low[MAXN];
long long cv_ctr,tp,root;
int vis[MAXN];
bool cv[MAXN];

void tarjan(int u){//let's go
    dfn[u] = low[u] = ++tp;
    int flag = 0;
    for(int i=head[u] ; i ; i=edge[i].nxt){
        int v=edge[i].to;
        if( !dfn[v] ) {
            tarjan(v);
            low[u] = min(low[u],low[v]);
            if(low[v] >= dfn[u]){
                flag++;
                if(u != root || flag > 1) {
                    if(cv[u] == false){
                        cv_ctr++;
                        cv[u] = true;
                    }
                }
            }
        }
        else low[u]=min(low[u],dfn[v]);
    }
}

void dfs(int x){
    NUM ++;
    vis[x] = color;
    
    for(int i=head[x];i;i=edge[i].nxt){
        int y=edge[i].to;
        if( cv[y] && vis[y] != color ) {
            vis[y] = color;
            CUT ++ ;
            continue;
        }
        else {
            if( !vis[y] && !cv[y] ) dfs(y);
        }
    }
    return ; 
}

int main(){
    int SDF=0;
    while(scanf("%d",&m) && m!=0){
        
        memset(edge,0,sizeof edge);
        memset(head,0,sizeof head);
        memset(low,0,sizeof low);
        memset(dfn,0,sizeof dfn);
        memset(cv,false,sizeof cv);
        memset(vis,0,sizeof vis);
        ans2=1;
        cv_ctr = tp = ans1  = root = 0;
        ectr = n = NUM = CUT = color = 0;
        ans2 = 1; 
        for(int i=1;i<=m;i++){
            int tra1=0,tra2=0;
            scanf("%d%d",&tra1,&tra2);
            addedge(tra1,tra2),addedge(tra2,tra1);
            n=max(n,tra1),n=max(n,tra2);
        }
        
        root = 1 ;
        tarjan(1);
        
        for(int i=1;i<=n;i++) {
            if( vis[i] || cv[i] ) continue;
            color ++ ;
            CUT = NUM = 0;
            dfs(i);
            if( CUT == 0 ) { 
                ans2*=(NUM - 1) * NUM /2;
                ans1 += 2 ;
            }
            if( CUT == 1 ) {
                ans2 *= NUM ;
                ans1 += 1 ;
            }
            if( CUT >= 2 ) {
                continue ; 
            }
        }
        ++SDF;
        cout<<"Case "<<SDF<<": ";
        cout<<ans1<<" "<<ans2<<endl;
    }
    return 0;
}

TAG : SIN_XIII ⑨

原文地址:https://www.cnblogs.com/SINXIII/p/10491451.html