[BZOJ 2730][HNOI 2012] 矿场搭建
2730: [HNOI2012]矿场搭建
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 2113 Solved: 979
[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)。
题意
给定一个无向图, 分配最少的救援通道使任意一个结点断开后所有其余结点都与至少一个救援通道连通.
题解
断开一个点还能保持连通...除了点双联通分量还有谁?
每个点双连通分量里都至少要有一个并且为了保证最少只能有一个. 由于每个双联通分量中任选一个安置救援通道即可, 所以最终的方案数为各双联通分量的大小之积.
$Tarjan$ 求割顶然后乱搞也可以过.
参考代码
1 #include <cstdio> 2 #include <cstring> 3 #include <queue> 4 5 const int MAXN=505; 6 typedef long long LL; 7 8 struct Edge{ 9 int to; 10 Edge* next; 11 }; 12 Edge E[MAXN]; 13 Edge* head[MAXN]; 14 Edge* top=E; 15 16 int n,m,len,rt,son,ans,cnt,tot,Node,Case=0,Time,dfn[MAXN],low[MAXN],vis[MAXN]; 17 LL f; 18 19 bool exist[MAXN],Gd[MAXN]; 20 21 void DFS(int); 22 void Tarjan(int); 23 void Initialize(); 24 void Insert(int,int); 25 int main() 26 { 27 while(scanf("%d",&m)==1){ 28 if(m==0) 29 break; 30 Initialize(); 31 for(int i=1;i<=m;i++){ 32 if(exist[i] && !dfn[i]){ 33 rt=i; 34 son=0; 35 Tarjan(i); 36 if(son>1 && !Gd[i]) 37 Gd[i]=true; 38 } 39 } 40 for(int i=1;i<=m+1;i++){ 41 if(!exist[i]) 42 continue; 43 if(!Gd[i]){ 44 cnt=0; 45 Node=1; 46 tot++; 47 DFS(i); 48 if(!cnt){ 49 ans+=2; 50 f=f*Node*1ll*(Node-1)/2; 51 } 52 else if(cnt==1){ 53 ans++; 54 f=Node*1ll*f; 55 } 56 } 57 } 58 printf("Case %d: %d %lld\n",Case,ans,f); 59 } 60 return 0; 61 } 62 63 void Initialize(){ 64 int x,y; 65 memset(head,0,sizeof head); 66 memset(exist,0,sizeof exist); 67 memset(dfn,0,sizeof dfn); 68 memset(low,0,sizeof low); 69 memset(Gd,0,sizeof Gd); 70 n=0; 71 len=0; 72 Time=0; 73 rt=-1; 74 f=1ll; 75 ans=0; 76 Case++; 77 78 for(int i=1;i<=m;i++){ 79 scanf("%d%d",&x,&y); 80 if(!exist[x]){ 81 exist[x]=1; 82 n++; 83 } 84 if(!exist[y]){ 85 exist[y]=1; 86 n++; 87 } 88 Insert(x,y); 89 Insert(y,x); 90 } 91 } 92 93 void DFS(int x){ 94 exist[x]=false; 95 for(Edge* i=head[x];i!=NULL;i=i->next){ 96 if(!exist[i->to]) 97 continue; 98 if(Gd[i->to] && vis[i->to]!=tot){ 99 vis[i->to]=tot; 100 cnt++; 101 } 102 else if(!Gd[i->to]){ 103 Node++; 104 DFS(i->to); 105 } 106 } 107 } 108 109 void Tarjan(int x){ 110 low[x]=dfn[x]=++Time; 111 for(Edge* i=head[x];i!=NULL;i=i->next){ 112 if(!dfn[i->to]){ 113 Tarjan(i->to); 114 if(x==rt) 115 son++; 116 else{ 117 low[x]=std::min(low[x],low[i->to]); 118 if(low[i->to]>=dfn[x] && !Gd[x]) 119 Gd[x]=true; 120 } 121 } 122 else low[x]=std::min(low[x],dfn[i->to]); 123 } 124 } 125 126 void Insert(int from,int to){ 127 top->to=to; 128 top->next=head[from]; 129 head[from]=top; 130 top++; 131 }
本博客已弃用, 新个人主页: https://rvalue.moe, 新博客: https://blog.rvalue.moe