[HNOI2012]矿场搭建
题目描述
煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。
请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。
输入输出格式
输入格式:输入文件有若干组数据,每组数据的第一行是一个正整数 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)。
存在2条点不重复的路径的连通分量就是点-双连通分量
时间戳求割点,从每个分量非割点出发遍历,到割点停下,将遍历的点加入
就可以求出所有点双连通
接下来就是最小的出口数
如果有一个连通分量有2个割点,那么就不需要出口,无论封哪个都可以到其他分量
如果只有一个割点,为了防止那个割点被封,要加一个出口
如果没有割点,就要修2个出口
方案数统计连通分量割点数讨论
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 typedef long long lol; 8 struct Node 9 { 10 int next,to; 11 }edge[200001]; 12 int head[10001],num,low[10001],dfn[10001],tot,root,iscut[10001],child,vis[10001],bcc_cnt,sum,n,N; 13 lol ans1,ans2; 14 void add(int u,int v) 15 { 16 num++; 17 edge[num].next=head[u]; 18 head[u]=num; 19 edge[num].to=v; 20 } 21 void tarjan(int x,int pa) 22 {int i; 23 low[x]=dfn[x]=++tot; 24 for (i=head[x];i;i=edge[i].next) 25 { 26 int v=edge[i].to; 27 if (dfn[v]==0) 28 { 29 tarjan(v,x); 30 low[x]=min(low[x],low[v]); 31 if (low[v]>=dfn[x]) 32 { 33 if (x!=root) 34 iscut[x]=1; 35 else child++; 36 } 37 } 38 else if (v!=pa) low[x]=min(low[x],dfn[v]); 39 } 40 } 41 void dfs(int x,int pa) 42 {int i; 43 vis[x]=bcc_cnt; 44 tot++; 45 for (i=head[x];i;i=edge[i].next) 46 { 47 int v=edge[i].to; 48 if (iscut[v]&&vis[v]!=bcc_cnt) sum++,vis[v]=bcc_cnt; 49 else 50 { 51 if (v!=pa&&vis[v]!=bcc_cnt) 52 dfs(v,x); 53 } 54 } 55 } 56 int main() 57 {int i,u,v; 58 int T=0; 59 while (cin>>n&&n) 60 { 61 num=0;N=0; 62 memset(iscut,0,sizeof(iscut)); 63 memset(head,0,sizeof(head)); 64 for (i=1;i<=n;i++) 65 { 66 scanf("%d%d",&u,&v); 67 add(u,v);add(v,u); 68 N=max(max(u,N),v); 69 } 70 bcc_cnt=0;tot=0; 71 memset(dfn,0,sizeof(dfn)); 72 memset(vis,0,sizeof(vis)); 73 memset(low,0,sizeof(low)); 74 for (i=1;i<=N;i++) 75 if (dfn[i]==0) 76 { 77 root=i; 78 tot=0;child=0; 79 tarjan(i,0); 80 if (child>=2) iscut[i]=1; 81 } 82 ans1=0;ans2=1; 83 for (i=1;i<=N;i++) 84 if (vis[i]==0&&iscut[i]==0) 85 { 86 sum=0; 87 tot=0;bcc_cnt++; 88 dfs(i,0); 89 if (sum==1) 90 ans1++,ans2=1ll*ans2*tot; 91 else if (sum==0) 92 ans1+=2,ans2=1ll*ans2*(tot-1)*tot/2; 93 } 94 printf("Case %d: %lld %lld\n",++T,ans1,ans2); 95 } 96 }