BZOJ2730 矿场搭建 解题报告 点双联通分量

题意概述:

  一张有向图,在其中设置一些关键点(即题目中的逃生出口),使得删除任意一个点之后其余点都可以到达至少一个关键点。

  问至少需要设置多少中关键点,有多少种设置方法。

解析:

  首先,这道题要求删掉一个点,不难想到这道题与割点有关。其次,删掉一个点其他点仍然可以到达关键点就可以想到是点双联通分量。

  但是,问题关键是,真的需要在每一个点双联通分量中都设置一个关键点吗?

  

答案是否定的,因为如果一个双联通分量连接了两个或两个以上的割点,一个割点被删掉那么还可以通过另外的割点到达某个关键点,如上图,红色点为割点,灰底色的边为割边

所以只需统计含割点个数小于等于1的块数就是最少的关键点个数,如此,则放置关键点的方案数为各个被统计的块(割点数小于等于1的块)的点的个数的乘积,当然,若只找到了一个满足条件的块,那最少关键点数为2,方案数为(令n为点的个数):  n*(n-1)/2.

需要注意的是这道题的数据规模:有500条边,但是并没有对点的编号的说明,所以点的标号可以是任意的,故此题建议离散化,但是我还是偷了个懒,因为数据中的点好像不超过50000

代码如下:

  1 #include <iostream>
  2 #include <algorithm>
  3 #include <cstdio>
  4 #include <cstring>
  5 #include <cstdlib>
  6 #include <cmath>
  7 #include <ctime>
  8 #include <vector>
  9
 10 using namespace std;
 11
 12 //#define    File
 13 //#define    Debug
 14
 15 struct Edge
 16 {
 17     int    to;
 18     int    next;
 19 }e[2100];
 20
 21 int    n,cnt,cnt_blocks,bcc_cnt,top,Ans,kase;
 22 int    p[51000],dfn[51000],nbcc[51000];
 23
 24 long long    Sum;
 25
 26 pair<int,int>    st[51000];
 27 vector<int>    bcc[1100];
 28
 29 bool    cut[51000];
 30
 31 inline    void    Add_edge(const int x,const int y)
 32 {
 33     e[++cnt].to=y;
 34     e[cnt].next=p[x];
 35     p[x]=cnt;
 36     return ;
 37 }
 38
 39 int    Tarjan(const int S,const int fa)
 40 {
 41     int    child,lowu,lowv,v,i;
 42
 43     dfn[S]=lowu=++cnt_blocks;
 44     child=0;
 45
 46     for(i=p[S];i;i=e[i].next)
 47     {
 48         v=e[i].to;
 49         if(!dfn[v])
 50         {
 51             st[++top]=make_pair(S,v);
 52             child++;
 53             lowv=Tarjan(v,S);
 54             lowu=min(lowv,lowu);
 55             if(lowv>=dfn[S])
 56             {
 57                 cut[S]=true;
 58                 bcc_cnt++;
 59                 bcc[bcc_cnt].clear();
 60                 while(true)
 61                 {
 62                     if(nbcc[st[top].first]!=bcc_cnt)
 63                     {
 64                         bcc[bcc_cnt].push_back(st[top].first);
 65                         nbcc[st[top].first]=bcc_cnt;
 66                     }
 67                     if(nbcc[st[top].second]!=bcc_cnt)
 68                     {
 69                         bcc[bcc_cnt].push_back(st[top].second);
 70                         nbcc[st[top].second]=bcc_cnt;
 71                     }
 72
 73                     if(st[top].first==S && st[top].second==v)
 74                         {top--;break;}
 75                     top--;
 76                 }
 77             }
 78         }
 79         else if(dfn[v]<dfn[S]  && v!=fa)
 80         {
 81             st[++top]=make_pair(S,e[i].to);
 82             lowu=min(lowu,dfn[v]);
 83         }
 84     }
 85
 86     if(fa<0 && child==1)cut[S]=false;
 87     return lowu;
 88 }
 89
 90 inline    void    Init()
 91 {
 92 /*int    n,cnt,cnt_blocks,bcc_cnt,top,Ans,kase;
 93 int    p[51000],dfn[51000],nbcc[51000];
 94
 95 long long    Sum;
 96
 97 pair<int,int>    st[51000];
 98 vector<int>    bcc[1100];
 99
100 bool    visited[51000],cut[51000];*/
101     cnt=cnt_blocks=bcc_cnt=top=Ans=0;
102     Sum=1;
103     memset(p,0,sizeof(p));
104     memset(dfn,0,sizeof(dfn));
105     memset(nbcc,0,sizeof(nbcc));
106     memset(cut,0,sizeof(cut));
107     for(int i=1;i<=1000;++i)
108         bcc[i].clear();
109     memset(st,0,sizeof(st));
110     return ;
111 }
112
113
114 int main()
115 {
116 #ifdef    File
117     freopen("2730.in","r",stdin);
118 #ifndef    Debug
119     freopen("2730.out","w",stdout);
120 #endif
121 #endif
122
123     int    i,j,x,y,cut_cnt;
124
125     while(~scanf("%d",&n) && n)
126     {
127         Init();
128         for(i=1;i<=n;++i)
129         {
130             scanf("%d%d",&x,&y);
131             Add_edge(x,y);
132             Add_edge(y,x);
133         }
134
135         for(i=1;i<=n;++i)
136         {
137             if(!dfn[i])Tarjan(i,-1);
138         }
139
140         for(i=1;i<=bcc_cnt;++i)
141         {
142             cut_cnt=0;
143             for(j=0;j<(int)bcc[i].size();++j)
144             {
145                 if(cut[bcc[i][j]])cut_cnt++;
146             }
147             if(cut_cnt==1)
148             {
149                 Ans++;
150                 Sum*=(long long)(bcc[i].size()-cut_cnt);
151             }
152         }
153
154         if(bcc_cnt==1)
155         {
156             Ans=2;
157             Sum=bcc[1].size()*(bcc[1].size()-1)/2;
158         }
159
160         printf("Case %d: %d %lld\n",++kase,Ans,Sum);
161     }
162
163 #ifdef    File
164     fclose(stdin);
165 #ifndef    Debug
166     fclose(stdout);
167 #endif
168 #endif
169
170     return 0;
171 }

时间: 2024-10-29 10:46:22

BZOJ2730 矿场搭建 解题报告 点双联通分量的相关文章

poj3694 network(边双联通分量+lca+并查集)

题    目    传    送    们    在    这 题目大意 有一个由n个点和m条边组成的无向联通图. 现在有Q个操作,每次操作可以在点x,y之间连一条边. 问你每次操作后有多少个多少个桥(即删掉后就会使图不联通的边). 解题思路 根据边双联通的定义,我们知道将边双联通分量缩点后的图,其中的边即为桥. 我们将这个图缩点,就变成了一棵树. 而每次在两个不同的边双联通分量x,y之间加边后,就出现了一个包含x,y的环,其中原先这颗树上x,y的树上最短路径就不在是边. 所以对于每个x,y,我

hihocoder #1190 : 连通性&#183;四 点双联通分量

http://hihocoder.com/problemset/problem/1190?sid=1051696 先抄袭一下 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho从约翰家回到学校时,网络所的老师又找到了小Hi和小Ho. 老师告诉小Hi和小Ho:之前的分组出了点问题,当服务器(上次是连接)发生宕机的时候,在同一组的服务器有可能连接不上,所以他们希望重新进行一次分组.这一次老师希望对连接进行分组,并把一个组内的所有连接关联的服务器也视为这个组内

[HDOJ4738]Caocao&#39;s Bridges(双联通分量,割边,tarjan)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4738 给一张无向图,每一条边都有权值.找一条割边,使得删掉这条边双连通分量数量增加,求权值最小那条. 注意有重边,ACEveryDay里群巨给的意见是tarjan的时候记录当前点是从哪条边来的. 注意假如桥的权值是0的时候也得有一个人去炸…… 1 /* 2 ━━━━━┒ギリギリ♂ eye! 3 ┓┏┓┏┓┃キリキリ♂ mind! 4 ┛┗┛┗┛┃\○/ 5 ┓┏┓┏┓┃ / 6 ┛┗┛┗┛┃ノ) 7

UVA - 10765 Doves and bombs (双联通分量)

链接 :  http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=34798 给N个点的无向图并且联通,问删除每次一个点之后还剩多少联通分量. 找割顶 如果删除的是割顶 联通分量就会增加,否则还是1(因为原图是联通图),删除割顶之后 联通块的数目 就要看该割顶在几个双联通分量里出现过. #pragma comment(linker, "/STACK:10240000,10240000") #include <a

【POJ 2942】Knights of the Round Table(双联通分量+染色判奇环)

[POJ 2942]Knights of the Round Table(双联通分量+染色判奇环) Time Limit: 7000MS   Memory Limit: 65536K Total Submissions: 11661   Accepted: 3824 Description Being a knight is a very attractive career: searching for the Holy Grail, saving damsels in distress, an

HDU5409---CRB and Graph 2015多校 双联通分量缩点

题意:一个联通的无向图, 对于每一条边, 若删除该边后存在两点不可达,则输出这两个点, 如果存在多个则输出第一个点尽可能大,第二个点尽可能小的. 不存在输出0 0 首先 若删除某一条边后存在多个联通分量则该边一定是桥, 那么我们可以先处理出所有的桥,然后把所有双联通分量缩点,缩点之后就变成了一棵树. 而树上的每一条边都是一个桥, 考虑每条边的输出,删除某一边后肯定会出现两个联通分量, 需要记录两个联通分量中最大的点max1 max2, 如果max1!=n 则答案就是max1 max1+1否则ma

POJ 1515 双联通分量

点击打开链接 题意:给一个联通的无向图,然后问你将其中的边变为有向的,加边使其变成有向的联通图 思路:若无向图有双联通分量,那么这个分量里的元素可以变成有向图的强联通,这应该很好看出来,然后需要加的边是什么呢,就是这个图上的桥呗,是桥的话变成有向的就要加一条边,然后剩下的无向图的双联通分量可以用dfs搜一下,边搜边输出就可以了,将桥记录下来遇到桥的时候特殊处理一下,然后双联通分量里的边每一条只能走一次,将走得边和反向边标记一下就行了  PS:vector写这样反向边的真是麻烦 #include

HDU 4612 双联通分量+树的直径

点击打开链接 题意:给一个无向联通图,里面可能有重边,问添加一条边后,使得图中的桥最小,将桥的数量输出 思路:刚刚读完题,就有了思路去写,无非就是将联通图双联通分量后缩点,然后求一条最长的路,首尾相连,肯定将更多的桥包含使得这些桥不再是桥,很好想的题,但是错了20+什么鬼,md重边这么难处理,醉了~~~,之前的做法是将重边全部找出来,希望数据弱点水过去算了,TLE好样的,那么我们在处理桥的时候,也就是找桥的时候,如果是桥,我们将这条边标记一下,然后找所有边时加上就行了,在一个就是找树的直径,两次

HDU 4738 Caocao&#39;s Bridges(双联通分量+并查集)

大意:有n座岛和m条桥,每条桥上有w个兵守着,现在要派不少于守桥的士兵数的人去炸桥,只能炸一条桥,使得这n座岛不连通,求最少要派多少人去. 思路:我们就是要缩点后直接求桥上人的最少数量.(PS:1.注意图如果不联通直接输出0.2.如果图中的桥上人为0,个那么要让一个人去.3.重边的问题.这里可以忽略) #include<map> #include<queue> #include<cmath> #include<cstdio> #include<stac