HNOI 2012 矿场搭建
题目描述 煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。 请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。 输入输出格式 输入格式: 输入文件有若干组数据,每组数据的第一行是一个正整数 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)。
芒果君:读完题我们先来想象一下,如果图上任意一点“爆”了,会发生什么情况呢?这时就会很自然的想到割点,割点一旦“爆”了,就会将图分成两个点双。先跑一遍tarjan将割点找出来吧,注意根节点要特判。之后对于每个点双进行dfs,统计大小和接触到割点的个数。没有割点,需要设两个出口(防止一个出口爆炸),方案用组合C(n,2);有一个割点,需要一个出口(防止割点爆炸);大于等于二直接无视,因为前两种情况已经解决了逃生的方案。程序还是有许多细节需要注意的,乘法原理、初始化等等。
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<iostream> 5 #include<algorithm> 6 #include<cstdlib> 7 #include<vector> 8 #include<queue> 9 #include<stack> 10 #define inf 1<<29 11 #define ll long long 12 #define maxn 510 13 using namespace std; 14 struct Edge{ 15 int u,v,ne; 16 }e[maxn*maxn]; 17 int n,m,cnt,ucnt,tot,rt,son,dfn[maxn],low[maxn],cut[maxn],hl[maxn],jh[maxn],T; 18 ll ans,bns; 19 void init() 20 { 21 memset(dfn,0,sizeof(dfn)); 22 memset(low,0,sizeof(low)); 23 memset(cut,0,sizeof(cut)); 24 memset(hl,0,sizeof(hl)); 25 memset(jh,0,sizeof(jh)); 26 n=ucnt=cnt=tot=ans=0; 27 bns=1; 28 } 29 void add(int u,int v) 30 { 31 e[++cnt].u=u; 32 e[cnt].v=v; 33 e[cnt].ne=hl[u]; 34 hl[u]=cnt; 35 } 36 void tarjan(int x,int fa) 37 { 38 dfn[x]=low[x]=++cnt; 39 for(int i=hl[x];i;i=e[i].ne){ 40 int v=e[i].v; 41 if(!dfn[v]){ 42 tarjan(v,x); 43 low[x]=min(low[x],low[v]); 44 if(low[v]>=dfn[x]){ 45 if(x==rt) son++; 46 else cut[x]=1; 47 } 48 } 49 else if(v!=fa) low[x]=min(low[x],dfn[v]); 50 } 51 } 52 void dfs(int x) 53 { 54 jh[x]=tot; 55 if(cut[x]) return; 56 cnt++; 57 for(int i=hl[x];i;i=e[i].ne){ 58 int v=e[i].v; 59 if(cut[v]&&jh[v]!=tot) jh[v]=tot,ucnt++; 60 if(!jh[v]) dfs(v); 61 } 62 } 63 int main() 64 { 65 int u,v; 66 while(1){ 67 scanf("%d",&m); 68 if(!m) break; 69 init(); 70 while(m--){ 71 scanf("%d%d",&u,&v); 72 n=max(n,max(u,v)); 73 add(u,v);add(v,u); 74 } 75 cnt=0; 76 for(int i=1;i<=n;++i){ 77 if(!dfn[i]) tarjan(rt=i,0); 78 if(son>=2) cut[rt]=1; 79 son=0; 80 } 81 for(int i=1;i<=n;++i){ 82 if((!jh[i])&&(!cut[i])){ 83 tot++;cnt=ucnt=0; 84 dfs(i); 85 if(!ucnt) ans+=2,bns*=cnt*(cnt-1)/2; 86 else if(ucnt==1) ans++,bns*=cnt; 87 } 88 } 89 printf("Case %d: %lld %lld\n",++T,ans,bns); 90 } 91 return 0; 92 }