【BZOJ-2730】矿场搭建 Tarjan 双连通分量

2730: [HNOI2012]矿场搭建

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 1602  Solved: 751
[Submit][Status][Discuss]

Description

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

Input

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

Output

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

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

Case 1 的四组解分别是(2,4),(3,4),(4,5),(4,6);

Case 2 的一组解为(4,5,6,7)。

Source

day1

Solution

对于删除一个点,其余点要有出路,显然是和割点有关,那么我们求出所有割点,以及双连通分量。

对于一个双联通分量中,如果我们只删除一个割点或非割点,那么如果还有其余割点能使其余点到关键点,那么显然是不需要额外考虑的;如果没有其余的割点令我们到关键点,那么我们需要新建额外的关键点

分情况讨论,如果一个双联通分量里,有两个及以上割点,那么这个双联通分量里面是不需要额外建的

如果一个双联通分量里只有一个或没有割点,那么我们只需要再建一个即可。

至于方案数,可以利用乘法原理,我们把每个连通分量里的每个可以用来建成关键点的点用乘法统计起来即可

Code

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define LL long long
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<‘0‘ || ch>‘9‘) {if (ch==‘-‘) f=-1; ch=getchar();}
    while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}
#define MAXM 1010
#define MAXN 80010
int N,M,tot;
LL sum;
struct EdgeNode{int next,to,from;}edge[MAXM<<1];
int head[MAXN],cnt=1;
void AddEdge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;}
void InsertEdge(int u,int v) {AddEdge(u,v); AddEdge(v,u);}
#define Pa pair<int,int>
vector<int>BCC[MAXN];
Pa st[MAXM]; int top;
int dfn[MAXN],low[MAXN],dfsn,cut[MAXN],bcc,belong[MAXN];
void Tarjan(int now,int last)
{
    dfn[now]=low[now]=++dfsn; int son=0;
    for (int i=head[now]; i; i=edge[i].next)
        if (!dfn[edge[i].to])
            {
                st[++top]=make_pair(now,edge[i].to);  son++;
                Tarjan(edge[i].to,now); low[now]=min(low[now],low[edge[i].to]);
                if (dfn[now]<=low[edge[i].to])
                    {
                        cut[now]=1; bcc++; BCC[bcc].clear(); int tnow=-1,tto=-1;
                        while (1)
                            {
                                tnow=st[top].first,tto=st[top].second; top--;
                                if (belong[tnow]!=bcc) BCC[bcc].push_back(tnow),belong[tnow]=bcc;
                                if (belong[tto]!=bcc) BCC[bcc].push_back(tto),belong[tto]=bcc;
                                if (tnow==now && tto==edge[i].to) break;
                            }
                    }
            }
        else if (dfn[edge[i].to]<dfn[now] && edge[i].to!=last)
            st[++top]=make_pair(now,edge[i].to),low[now]=min(low[now],dfn[edge[i].to]);
    if (last<0 && son==1) cut[now]=0;
}
int main()
{
    int cas=0;
    while (scanf("%d",&M))
        {
            if (M==0) break;
            N=0; cnt=1; memset(head,0,sizeof(head)); top=0; bcc=0; memset(st,0,sizeof(st));
            memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); bcc=0,dfsn=0;
            memset(cut,0,sizeof(cut)); memset(belong,0,sizeof(belong));

            for (int x,y,i=1; i<=M; i++) x=read(),y=read(),InsertEdge(x,y),N=max(N,max(x,y));
            for (int i=1; i<=N; i++) if (!dfn[i]) Tarjan(i,-1);
//            for (int i=1; i<=N; i++) printf("%d %d %d\n",dfn[i],belong[i],cut[i]);
            tot=0,sum=1;
//            printf("%d\n",bcc);
//            for (int i=1; i<=bcc; i++) printf("%d ",BCC[i].size()); puts("");
            for (int i=1,num=0,sz=BCC[i].size()-1; i<=bcc; i++,num=0,sz=BCC[i].size()-1)
                {
//                    printf("%d %d %I64d\n",num,tot,sum);
                    for (int j=0; j<=sz; j++) if (cut[BCC[i][j]]) num++;
                    if (num==1) tot++,sum*=(LL)(BCC[i].size()-num);
                    if (bcc==1) {tot=2; sum=(LL)BCC[1].size()*(BCC[1].size()-1)/2; break;}
                }
            printf("Case %d: %d %lld\n",++cas,tot,sum);
        }
    return 0;
}
时间: 2024-12-28 01:46:46

【BZOJ-2730】矿场搭建 Tarjan 双连通分量的相关文章

BZOJ 2730 矿场搭建(割点)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2730 题意:煤矿工地可以看成是由隧道连接挖煤点 组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个 挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口.计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数. 思路:首先求割点:那么删掉割点后将形成若干连通块.若某连通块只与一个

BZOJ 2730 HNOI2012 矿场搭建 Tarjan

题目大意:给定一个无向图,要求将一些点设为出口 要求图中删掉任意一个点后剩余的任意一个点都与至少一个出口相连 求最少建多少个出口以及建最少出口的方案数 首先看到割点就是Tarjan搞 但是怎么搞 首先假设我们把所有的点双都缩点 那么我们一定可以得到一棵树 然后我们就会发现 叶子节点(只含有一个割点的点双)必须建 因为叶子节点如果不建 一旦割点被爆就死翘了 非叶节点(含有两个或两个以上的割点的点双)不用建 因为即使一个割点被爆了也可以沿着另一个割点走到一个叶节点 还有一种情况就是整个联通块都是点双

[BZOJ 2730][HNOI 2012] 矿场搭建

2730: [HNOI2012]矿场搭建 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2113  Solved: 979[Submit][Status][Discuss] Description 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口.请写一个程序,用

【BZOJ】【2730】【HNOI2012】矿场搭建

Tarjan求BCC/割点 然而似乎我一开始抄的白书的板子哪里抄错了?还是本身哪里不对……(可能是不适用于这道题?因为这题要求求出每个BCC的大小..? 膜拜了ydc的写法= = 其实两次dfs也并没有比lrj的麻烦到哪里去……感觉反而更清晰易懂,不容易出bug 大家都是NOIP之前就会求割点了……只有我比较傻逼…… 核心是 $low[son]\geq dfn[fa] \Rightarrow fa 是割点$ 嗯如果只有一个BCC那么只需要两个出口,然后如果一个BCC连接着两个(及以上?)割点(这

BZOJ 2730:[HNOI2012]矿场搭建(割点+连通块)

[HNOI2012]矿场搭建 Description 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口.请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数.Input 输入文件有若干组数据,每组数据的第一行是一个正整数 N(N≤500),表示工地的隧道数,接下来的 N 行每行是用空

bzoj 2730: [HNOI2012]矿场搭建

Description 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口.请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数. Input 输入文件有若干组数据,每组数据的第一行是一个正整数 N(N≤500),表示工地的隧道数,接下来的 N 行每行是用空格隔开的两个整数 S 和 T

【双连通分量】Bzoj2730 HNOI2012 矿场搭建

Description 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口.请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数. Sulotion 坍塌的不是割顶并不影响,建在割顶也没有意义(拆割顶) 考虑每一个双连通分量,如果只连一个割顶必须内部建一个(随便选一个),连两个以上可以

POJ 3352 Road Construction(边双连通分量,桥,tarjan)

题解转自http://blog.csdn.net/lyy289065406/article/details/6762370   文中部分思路或定义模糊,重写的红色部分为修改过的. 大致题意: 某个企业想把一个热带天堂岛变成旅游胜地,岛上有N个旅游景点,保证任意2个旅游景点之间有路径连通的(可间接连通).而为了给游客提供更方便的服务,该企业要求道路部门在某些道路增加一些设施. 道路部门每次只会选择一条道路施工,在该条道路施工完毕前,其他道路依然可以通行.然而有道路部门正在施工的道路,在施工完毕前是

连通分量模板:tarjan: 求割点 &amp;&amp; 桥 &amp;&amp; 缩点 &amp;&amp; 强连通分量 &amp;&amp; 双连通分量 &amp;&amp; LCA(最近公共祖先)

PS:摘自一不知名的来自大神. 1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点. 2.割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个连通块,就称这个点集为割点集合. 3.点连通度:最小割点集合中的顶点数. 4.割边(桥):删掉它之后,图必然会分裂为两个或两个以上的子图. 5.割边集合:如果有一个边集合,删除这个边集合以后,原图变成多个连通块,就称这个点集为割边集合. 6.边连通度:一个图的边连通度的定义为,最