洛谷—— P3225 [HNOI2012]矿场搭建
https://www.luogu.org/problem/show?pid=3225
题目描述
煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。
请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。
输入输出格式
输入格式:
输入文件有若干组数据,每组数据的第一行是一个正整数 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
说明
Case 1 的四组解分别是(2,4),(3,4),(4,5),(4,6);
Case 2 的一组解为(4,5,6,7)。
很明显需要求出割点。屏蔽割点后、
①若一个双联通中没有割点,那么最少需要两个出口(炸了一个去另一个)
②若一个双联通能连到一个割点,那么最少需要在双联通中设置一个出口,炸了割点还能逃生、
③若一个双联通能连到多余一个割点,那么不管怎么炸,都能从没炸的割点跑到别的分量里
1 #include <cstring> 2 #include <cstdio> 3 4 const int N(555); 5 int head[N],sumedge=1; 6 struct Edge 7 { 8 int v,next; 9 Edge(int v=0,int next=0):v(v),next(next){} 10 }edge[N*N/2]; 11 inline void ins(int u,int v) 12 { 13 edge[++sumedge]=Edge(v,head[u]); 14 head[u]=sumedge; 15 edge[++sumedge]=Edge(u,head[v]); 16 head[v]=sumedge; 17 } 18 19 #define max(a,b) (a>b?a:b) 20 #define min(a,b) (a<b?a:b) 21 int tim,dfn[N],low[N]; 22 int cutpoint[N]; 23 void Tarjan(int u,int pre) 24 { 25 dfn[u]=low[u]=++tim; 26 int sumtredge=0,if_point=0; 27 for(int v,i=head[u];i;i=edge[i].next) 28 { 29 v=edge[i].v; 30 if((i^1)==pre) continue; 31 if(!dfn[v]) 32 { 33 sumtredge++; 34 Tarjan(v,i); 35 if(low[v]>=dfn[u]) if_point=1; 36 low[u]=min(low[u],low[v]); 37 } 38 else low[u]=min(low[u],dfn[v]); 39 } 40 if(!pre) 41 { 42 if(sumtredge>1) cutpoint[u]=1; 43 } 44 else if(if_point) cutpoint[u]=1; 45 } 46 47 int sumcol,point,cnt,vis[N],col[N]; 48 void DFS(int u) 49 { 50 point++; 51 vis[u]=1; 52 col[u]=sumcol; 53 for(int v,i=head[u];i;i=edge[i].next) 54 { 55 v=edge[i].v; 56 if(cutpoint[v]&&col[v]!=sumcol) cnt++,col[v]=sumcol; 57 if(!vis[v]&&!cutpoint[v]) DFS(v); 58 } 59 } 60 61 inline void init() 62 { 63 tim=sumcol=0; sumedge=1; 64 memset(dfn,0,sizeof(dfn)); 65 memset(col,0,sizeof(col)); 66 memset(vis,0,sizeof(vis)); 67 memset(low,0,sizeof(low)); 68 memset(head,0,sizeof(head)); 69 memset(edge,0,sizeof(edge)); 70 memset(cutpoint,0,sizeof(cutpoint)); 71 } 72 73 int main() 74 { 75 for(int m,n=0,k=1;scanf("%d",&m)&&m;k++,n=0,init()) 76 { 77 for(int u,v;m--;ins(u,v)) 78 scanf("%d%d",&u,&v),n=max(n,max(u,v)); 79 for(int i=1;i<=n;i++) 80 if(!dfn[i]) Tarjan(i,0); 81 long long ans_sum=1; 82 int ans_cnt=0; 83 for(int i=1;i<=n;i++) 84 { 85 if(vis[i]||cutpoint[i]) continue; 86 point=cnt=0; 87 sumcol++,DFS(i); 88 if(!cnt) ans_cnt+=2,ans_sum*=(long long)(point*(point-1)>>1); 89 if(cnt==1) ans_cnt++,ans_sum*=(long long)point; 90 } 91 printf("Case %d: %d %lld\n",k,ans_cnt,ans_sum); 92 } 93 return 0; 94 }
——每当你想要放弃的时候,就想想是为了什么才一路坚持到现在。