bzoj 2730: [HNOI2012]矿场搭建

Description

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

Input

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

Output

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

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

Case1 的四组解分别是 (2,4),(3,4),(4,5),(4,6)

Case2 的一组解为 (4,5,6,7)

Source

day1


solution

请移步PoPoQQQ巨佬的题解

注意开long long 和输出格式

太懒了不想写了

code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;

template<typename T>
void input(T &x) {
    x=0; T a=1;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar())
        if(c=='-') a=-1;
    for(;c>='0'&&c<='9';c=getchar())
        x=x*10+c-'0';
    x*=a;
    return;
}

#define MAXM 510
#define MAXN 510

struct Edge {
    int u,v,next;
    Edge(int u=0,int v=0,int next=0):
        u(u),v(v),next(next) {}
};

Edge edge[MAXM<<1];
int head[MAXN],cnt;

void addedge(int u,int v) {
    edge[++cnt]=Edge(u,v,head[u]);
    head[u]=cnt;
    return;
}

int dfn[MAXN],low[MAXN],timee;
bool cutpoint[MAXN];
int root,sum;

void tarjan(int now,int f) {
    dfn[now]=low[now]=++timee;
    for(int i=head[now];i;i=edge[i].next) {
        int v=edge[i].v;
        if(!dfn[v]) {
            tarjan(v,now);
            low[now]=min(low[now],low[v]);
            if(low[v]>=dfn[now]) {
                if(now!=root)
                    cutpoint[now]=true;
                else sum++;
            }
        } else if(v!=f)
            low[now]=min(low[now],dfn[v]);
    }
    return;
}

int col[MAXN],numcolor;
int cutsum,notcutsum;

void dfs(int now) {
    col[now]=numcolor;
    notcutsum++;
    for(int i=head[now];i;i=edge[i].next) {
        int v=edge[i].v;
        if(cutpoint[v]&&col[v]!=numcolor) {
            cutsum++;
            col[v]=numcolor;
        }
        if(!col[v]) dfs(v);
    }
    return;
}

void _clear() {
    memset(head,0,sizeof(head));
    memset(col,0,sizeof(col));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(cutpoint,0,sizeof(cutpoint));
    cnt=timee=numcolor=0;
}

int main() {
    for(int Case=1;true;Case++) {
        _clear();
        int n=0,m;
        input(m);
        if(m==0) break;
        for(int i=1;i<=m;i++) {
            int u,v;
            input(u),input(v);
            addedge(u,v),
            addedge(v,u);
            n=max(n,u),n=max(n,v);
        }
        for(int i=1;i<=n;i++)
            if(!dfn[i]) {
                sum=0,root=i;
                tarjan(i,i);
                if(sum>=2)
                    cutpoint[i]=true;
            }
        int ansA=0;
        ll ansB=1;
        for(int i=1;i<=n;i++)
            if(!col[i]&&!cutpoint[i]) {
                numcolor++;
                cutsum=notcutsum=0;
                dfs(i);
                if(cutsum==0) {
                    ansA+=2;
                    ansB*=1ll*(notcutsum-1)*notcutsum/2;
                }
                if(cutsum==1) {
                    ansA++;
                    ansB*=1ll*notcutsum;
                }
            }
        printf("Case %d: %d %lld\n",Case,ansA,ansB);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值